├── .gitattributes ├── 9781484261309.jpg ├── Contributing.md ├── LICENSE.txt ├── README.md ├── chapter03-master ├── README.md ├── multiplication │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── .DS_Store │ │ ├── main │ │ ├── .DS_Store │ │ ├── java │ │ │ ├── .DS_Store │ │ │ └── microservices │ │ │ │ ├── .DS_Store │ │ │ │ └── book │ │ │ │ ├── .DS_Store │ │ │ │ └── multiplication │ │ │ │ ├── MultiplicationApplication.java │ │ │ │ ├── challenge │ │ │ │ ├── Challenge.java │ │ │ │ ├── ChallengeAttempt.java │ │ │ │ ├── ChallengeAttemptController.java │ │ │ │ ├── ChallengeAttemptDTO.java │ │ │ │ ├── ChallengeController.java │ │ │ │ ├── ChallengeGeneratorService.java │ │ │ │ ├── ChallengeGeneratorServiceImpl.java │ │ │ │ ├── ChallengeService.java │ │ │ │ └── ChallengeServiceImpl.java │ │ │ │ └── user │ │ │ │ └── User.java │ │ └── resources │ │ │ ├── .DS_Store │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── microservices │ │ └── book │ │ └── multiplication │ │ ├── MultiplicationApplicationTests.java │ │ └── challenge │ │ ├── ChallengeAttemptControllerTest.java │ │ ├── ChallengeGeneratorServiceTest.java │ │ └── ChallengeServiceTest.java └── resources │ ├── app-layers-chapter3.png │ └── business_model.png ├── chapter04-master ├── README.md ├── challenges-frontend │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ └── ChallengeComponent.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── serviceWorker.js │ │ ├── services │ │ └── ApiClient.js │ │ └── setupTests.js ├── multiplication │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── .DS_Store │ │ ├── main │ │ ├── .DS_Store │ │ ├── java │ │ │ ├── .DS_Store │ │ │ └── microservices │ │ │ │ ├── .DS_Store │ │ │ │ └── book │ │ │ │ ├── .DS_Store │ │ │ │ └── multiplication │ │ │ │ ├── MultiplicationApplication.java │ │ │ │ ├── challenge │ │ │ │ ├── Challenge.java │ │ │ │ ├── ChallengeAttempt.java │ │ │ │ ├── ChallengeAttemptController.java │ │ │ │ ├── ChallengeAttemptDTO.java │ │ │ │ ├── ChallengeController.java │ │ │ │ ├── ChallengeGeneratorService.java │ │ │ │ ├── ChallengeGeneratorServiceImpl.java │ │ │ │ ├── ChallengeService.java │ │ │ │ └── ChallengeServiceImpl.java │ │ │ │ ├── configuration │ │ │ │ └── WebConfiguration.java │ │ │ │ └── user │ │ │ │ └── User.java │ │ └── resources │ │ │ ├── .DS_Store │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── microservices │ │ └── book │ │ └── multiplication │ │ ├── MultiplicationApplicationTests.java │ │ └── challenge │ │ ├── ChallengeAttemptControllerTest.java │ │ ├── ChallengeGeneratorServiceTest.java │ │ └── ChallengeServiceTest.java └── resources │ ├── app-layers-chapter4.png │ └── chapter4_app_screenshot.png ├── chapter05-master ├── README.md ├── challenges-frontend │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── ChallengeComponent.js │ │ └── LastAttemptsComponent.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── serviceWorker.js │ │ ├── services │ │ └── ApiClient.js │ │ └── setupTests.js ├── multiplication │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── microservices │ │ │ │ └── book │ │ │ │ └── multiplication │ │ │ │ ├── MultiplicationApplication.java │ │ │ │ ├── challenge │ │ │ │ ├── Challenge.java │ │ │ │ ├── ChallengeAttempt.java │ │ │ │ ├── ChallengeAttemptController.java │ │ │ │ ├── ChallengeAttemptDTO.java │ │ │ │ ├── ChallengeAttemptRepository.java │ │ │ │ ├── ChallengeController.java │ │ │ │ ├── ChallengeGeneratorService.java │ │ │ │ ├── ChallengeGeneratorServiceImpl.java │ │ │ │ ├── ChallengeService.java │ │ │ │ └── ChallengeServiceImpl.java │ │ │ │ ├── configuration │ │ │ │ ├── JsonConfiguration.java │ │ │ │ └── WebConfiguration.java │ │ │ │ └── user │ │ │ │ ├── User.java │ │ │ │ └── UserRepository.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── microservices │ │ └── book │ │ └── multiplication │ │ ├── MultiplicationApplicationTests.java │ │ └── challenge │ │ ├── ChallengeAttemptControllerTest.java │ │ ├── ChallengeGeneratorServiceTest.java │ │ └── ChallengeServiceTest.java └── resources │ ├── app-last-attempts-screenshot.png │ ├── app-layers-chapter5.png │ └── data_model.png ├── chapter06-master ├── README.md ├── challenges-frontend │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── ChallengeComponent.js │ │ ├── LastAttemptsComponent.js │ │ └── LeaderBoardComponent.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── serviceWorker.js │ │ ├── services │ │ ├── ChallengesApiClient.js │ │ └── GameApiClient.js │ │ └── setupTests.js ├── gamification │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── game-layers.puml │ ├── game_layers.png │ ├── leaderboard-layers.puml │ ├── leaderboard_layers.png │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── microservices │ │ │ │ └── book │ │ │ │ └── gamification │ │ │ │ ├── GamificationApplication.java │ │ │ │ ├── challenge │ │ │ │ └── ChallengeSolvedDTO.java │ │ │ │ ├── configuration │ │ │ │ ├── JsonConfiguration.java │ │ │ │ └── WebConfiguration.java │ │ │ │ └── game │ │ │ │ ├── BadgeRepository.java │ │ │ │ ├── GameController.java │ │ │ │ ├── GameService.java │ │ │ │ ├── GameServiceImpl.java │ │ │ │ ├── LeaderBoardController.java │ │ │ │ ├── LeaderBoardService.java │ │ │ │ ├── LeaderBoardServiceImpl.java │ │ │ │ ├── ScoreRepository.java │ │ │ │ ├── badgeprocessors │ │ │ │ ├── BadgeProcessor.java │ │ │ │ ├── BronzeBadgeProcessor.java │ │ │ │ ├── FirstWonBadgeProcessor.java │ │ │ │ ├── GoldBadgeProcessor.java │ │ │ │ ├── LuckyNumberBadgeProcessor.java │ │ │ │ └── SilverBadgeProcessor.java │ │ │ │ └── domain │ │ │ │ ├── BadgeCard.java │ │ │ │ ├── BadgeType.java │ │ │ │ ├── LeaderBoardRow.java │ │ │ │ └── ScoreCard.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── microservices │ │ └── book │ │ └── gamification │ │ ├── GamificationApplicationTests.java │ │ └── game │ │ ├── GameServiceImplTest.java │ │ ├── LeaderBoardControllerTest.java │ │ ├── LeaderBoardServiceImplTest.java │ │ └── badgeprocessors │ │ ├── BronzeBadgeProcessorTest.java │ │ ├── FirstWonBadgeProcessorTest.java │ │ ├── GoldBadgeProcessorTest.java │ │ ├── LuckyNumberBadgeProcessorTest.java │ │ └── SilverBadgeProcessorTest.java ├── multiplication │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── microservices │ │ │ │ └── book │ │ │ │ └── multiplication │ │ │ │ ├── MultiplicationApplication.java │ │ │ │ ├── challenge │ │ │ │ ├── Challenge.java │ │ │ │ ├── ChallengeAttempt.java │ │ │ │ ├── ChallengeAttemptController.java │ │ │ │ ├── ChallengeAttemptDTO.java │ │ │ │ ├── ChallengeAttemptRepository.java │ │ │ │ ├── ChallengeController.java │ │ │ │ ├── ChallengeGeneratorService.java │ │ │ │ ├── ChallengeGeneratorServiceImpl.java │ │ │ │ ├── ChallengeService.java │ │ │ │ ├── ChallengeServiceImpl.java │ │ │ │ └── ChallengeSolvedDTO.java │ │ │ │ ├── configuration │ │ │ │ ├── JsonConfiguration.java │ │ │ │ └── WebConfiguration.java │ │ │ │ ├── serviceclients │ │ │ │ └── GamificationServiceClient.java │ │ │ │ └── user │ │ │ │ ├── User.java │ │ │ │ ├── UserController.java │ │ │ │ └── UserRepository.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── microservices │ │ └── book │ │ └── multiplication │ │ ├── MultiplicationApplicationTests.java │ │ └── challenge │ │ ├── ChallengeAttemptControllerTest.java │ │ ├── ChallengeGeneratorServiceTest.java │ │ ├── ChallengeServiceTest.java │ │ └── UserControllerTest.java └── resources │ ├── app-screenshot-leaderboard-1b.png │ ├── business_model-Chapter6.png │ └── logical_views-Chapter6c.png ├── chapter07-master ├── README.md ├── challenges-frontend │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── ChallengeComponent.js │ │ ├── LastAttemptsComponent.js │ │ └── LeaderBoardComponent.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── serviceWorker.js │ │ ├── services │ │ ├── ChallengesApiClient.js │ │ └── GameApiClient.js │ │ └── setupTests.js ├── gamification │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── game-layers.puml │ ├── game_layers.png │ ├── leaderboard-layers.puml │ ├── leaderboard_layers.png │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── microservices │ │ │ │ └── book │ │ │ │ └── gamification │ │ │ │ ├── GamificationApplication.java │ │ │ │ ├── challenge │ │ │ │ └── ChallengeSolvedEvent.java │ │ │ │ ├── configuration │ │ │ │ ├── AMQPConfiguration.java │ │ │ │ ├── JsonConfiguration.java │ │ │ │ └── WebConfiguration.java │ │ │ │ └── game │ │ │ │ ├── BadgeRepository.java │ │ │ │ ├── GameEventHandler.java │ │ │ │ ├── GameService.java │ │ │ │ ├── GameServiceImpl.java │ │ │ │ ├── LeaderBoardController.java │ │ │ │ ├── LeaderBoardService.java │ │ │ │ ├── LeaderBoardServiceImpl.java │ │ │ │ ├── ScoreRepository.java │ │ │ │ ├── badgeprocessors │ │ │ │ ├── BadgeProcessor.java │ │ │ │ ├── BronzeBadgeProcessor.java │ │ │ │ ├── FirstWonBadgeProcessor.java │ │ │ │ ├── GoldBadgeProcessor.java │ │ │ │ ├── LuckyNumberBadgeProcessor.java │ │ │ │ └── SilverBadgeProcessor.java │ │ │ │ └── domain │ │ │ │ ├── BadgeCard.java │ │ │ │ ├── BadgeType.java │ │ │ │ ├── LeaderBoardRow.java │ │ │ │ └── ScoreCard.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── microservices │ │ └── book │ │ └── gamification │ │ ├── GamificationApplicationTests.java │ │ └── game │ │ ├── GameServiceImplTest.java │ │ ├── LeaderBoardControllerTest.java │ │ ├── LeaderBoardServiceImplTest.java │ │ └── badgeprocessors │ │ ├── BronzeBadgeProcessorTest.java │ │ ├── FirstWonBadgeProcessorTest.java │ │ ├── GoldBadgeProcessorTest.java │ │ ├── LuckyNumberBadgeProcessorTest.java │ │ └── SilverBadgeProcessorTest.java ├── multiplication │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── microservices │ │ │ │ └── book │ │ │ │ └── multiplication │ │ │ │ ├── MultiplicationApplication.java │ │ │ │ ├── challenge │ │ │ │ ├── Challenge.java │ │ │ │ ├── ChallengeAttempt.java │ │ │ │ ├── ChallengeAttemptController.java │ │ │ │ ├── ChallengeAttemptDTO.java │ │ │ │ ├── ChallengeAttemptRepository.java │ │ │ │ ├── ChallengeController.java │ │ │ │ ├── ChallengeEventPub.java │ │ │ │ ├── ChallengeGeneratorService.java │ │ │ │ ├── ChallengeGeneratorServiceImpl.java │ │ │ │ ├── ChallengeService.java │ │ │ │ ├── ChallengeServiceImpl.java │ │ │ │ └── ChallengeSolvedEvent.java │ │ │ │ ├── configuration │ │ │ │ ├── AMQPConfiguration.java │ │ │ │ ├── JsonConfiguration.java │ │ │ │ └── WebConfiguration.java │ │ │ │ └── user │ │ │ │ ├── User.java │ │ │ │ ├── UserController.java │ │ │ │ └── UserRepository.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── microservices │ │ └── book │ │ └── multiplication │ │ ├── MultiplicationApplicationTests.java │ │ └── challenge │ │ ├── ChallengeAttemptControllerTest.java │ │ ├── ChallengeEventPubTest.java │ │ ├── ChallengeGeneratorServiceTest.java │ │ ├── ChallengeServiceTest.java │ │ └── UserControllerTest.java └── resources │ ├── app-chapter7-after10.png │ └── logical_views-Chapter7-detailed.png ├── chapter08d-master ├── README.md ├── challenges-frontend │ ├── Dockerfile │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── components │ │ ├── ChallengeComponent.js │ │ ├── LastAttemptsComponent.js │ │ └── LeaderBoardComponent.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── serviceWorker.js │ │ ├── services │ │ ├── ChallengesApiClient.js │ │ └── GameApiClient.js │ │ └── setupTests.js ├── docker │ ├── consul │ │ ├── Dockerfile │ │ └── consul-kv-docker.json │ ├── docker-compose-public.yml │ ├── docker-compose.yml │ └── docker.iml ├── gamification │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── game-layers.puml │ ├── leaderboard-layers.puml │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── microservices │ │ │ │ └── book │ │ │ │ └── gamification │ │ │ │ ├── GamificationApplication.java │ │ │ │ ├── challenge │ │ │ │ └── ChallengeSolvedEvent.java │ │ │ │ ├── configuration │ │ │ │ ├── AMQPConfiguration.java │ │ │ │ └── JsonConfiguration.java │ │ │ │ └── game │ │ │ │ ├── BadgeRepository.java │ │ │ │ ├── GameEventHandler.java │ │ │ │ ├── GameService.java │ │ │ │ ├── GameServiceImpl.java │ │ │ │ ├── LeaderBoardController.java │ │ │ │ ├── LeaderBoardService.java │ │ │ │ ├── LeaderBoardServiceImpl.java │ │ │ │ ├── ScoreRepository.java │ │ │ │ ├── badgeprocessors │ │ │ │ ├── BadgeProcessor.java │ │ │ │ ├── BronzeBadgeProcessor.java │ │ │ │ ├── FirstWonBadgeProcessor.java │ │ │ │ ├── GoldBadgeProcessor.java │ │ │ │ ├── LuckyNumberBadgeProcessor.java │ │ │ │ └── SilverBadgeProcessor.java │ │ │ │ └── domain │ │ │ │ ├── BadgeCard.java │ │ │ │ ├── BadgeType.java │ │ │ │ ├── LeaderBoardRow.java │ │ │ │ └── ScoreCard.java │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── bootstrap.properties │ │ │ └── logback-spring.xml │ │ └── test │ │ ├── java │ │ └── microservices │ │ │ └── book │ │ │ └── gamification │ │ │ ├── GamificationApplicationTests.java │ │ │ └── game │ │ │ ├── GameServiceImplTest.java │ │ │ ├── LeaderBoardControllerTest.java │ │ │ ├── LeaderBoardServiceImplTest.java │ │ │ └── badgeprocessors │ │ │ ├── BronzeBadgeProcessorTest.java │ │ │ ├── FirstWonBadgeProcessorTest.java │ │ │ ├── GoldBadgeProcessorTest.java │ │ │ ├── LuckyNumberBadgeProcessorTest.java │ │ │ └── SilverBadgeProcessorTest.java │ │ └── resources │ │ └── test.properties ├── gateway │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── microservices │ │ │ │ └── book │ │ │ │ └── gateway │ │ │ │ └── GatewayApplication.java │ │ └── resources │ │ │ ├── application.yml │ │ │ ├── bootstrap.yml │ │ │ └── logback-spring.xml │ │ └── test │ │ ├── java │ │ └── microservices │ │ │ └── book │ │ │ └── gateway │ │ │ └── GatewayApplicationTests.java │ │ └── resources │ │ └── test.properties ├── logs │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── microservices │ │ │ │ └── book │ │ │ │ └── logs │ │ │ │ ├── AMQPConfiguration.java │ │ │ │ ├── LogsApplication.java │ │ │ │ └── LogsConsumer.java │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── bootstrap.properties │ │ │ └── logback-spring.xml │ │ └── test │ │ ├── java │ │ └── microservices │ │ │ └── book │ │ │ └── logs │ │ │ └── LogsApplicationTests.java │ │ └── resources │ │ └── test.properties ├── multiplication │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── Dockerfile │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── microservices │ │ │ │ └── book │ │ │ │ └── multiplication │ │ │ │ ├── MultiplicationApplication.java │ │ │ │ ├── challenge │ │ │ │ ├── Challenge.java │ │ │ │ ├── ChallengeAttempt.java │ │ │ │ ├── ChallengeAttemptController.java │ │ │ │ ├── ChallengeAttemptDTO.java │ │ │ │ ├── ChallengeAttemptRepository.java │ │ │ │ ├── ChallengeController.java │ │ │ │ ├── ChallengeEventPub.java │ │ │ │ ├── ChallengeGeneratorService.java │ │ │ │ ├── ChallengeGeneratorServiceImpl.java │ │ │ │ ├── ChallengeService.java │ │ │ │ ├── ChallengeServiceImpl.java │ │ │ │ └── ChallengeSolvedEvent.java │ │ │ │ ├── configuration │ │ │ │ ├── AMQPConfiguration.java │ │ │ │ └── JsonConfiguration.java │ │ │ │ └── user │ │ │ │ ├── User.java │ │ │ │ ├── UserController.java │ │ │ │ └── UserRepository.java │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── bootstrap.properties │ │ │ └── logback-spring.xml │ │ └── test │ │ ├── java │ │ └── microservices │ │ │ └── book │ │ │ └── multiplication │ │ │ ├── MultiplicationApplicationTests.java │ │ │ └── challenge │ │ │ ├── ChallengeAttemptControllerTest.java │ │ │ ├── ChallengeEventPubTest.java │ │ │ ├── ChallengeGeneratorServiceTest.java │ │ │ ├── ChallengeServiceTest.java │ │ │ └── UserControllerTest.java │ │ └── resources │ │ └── test.properties └── resources │ ├── microservice_patterns-Config-Server.png │ └── microservice_patterns-View-Containers.png ├── cucumber-tests-master ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── README.md ├── docs │ ├── components_plantuml.png │ ├── components_plantuml.puml │ ├── cucumber_project_uml.png │ └── diagram.uml ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ └── test │ ├── java │ └── microservices │ │ └── book │ │ ├── RunCucumberTest.java │ │ └── cucumber │ │ ├── actors │ │ ├── Challenge.java │ │ └── Leaderboard.java │ │ ├── api │ │ ├── APIClient.java │ │ └── dtos │ │ │ ├── challenge │ │ │ ├── AttemptRequestDTO.java │ │ │ ├── AttemptResponseDTO.java │ │ │ └── ChallengeDTO.java │ │ │ ├── leaderboard │ │ │ └── LeaderboardRowDTO.java │ │ │ └── users │ │ │ └── UserDTO.java │ │ └── steps │ │ ├── ChallengeStepDefinitions.java │ │ └── GameStepDefinitions.java │ └── resources │ ├── cucumber.properties │ └── microservices │ └── book │ ├── leaderboard.feature │ └── solving_challenges.feature └── errata.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /9781484261309.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/9781484261309.jpg -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/README.md -------------------------------------------------------------------------------- /chapter03-master/multiplication/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter03-master/multiplication/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter03-master/multiplication/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter03-master/multiplication/src/.DS_Store -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter03-master/multiplication/src/main/.DS_Store -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter03-master/multiplication/src/main/java/.DS_Store -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter03-master/multiplication/src/main/java/microservices/.DS_Store -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter03-master/multiplication/src/main/java/microservices/book/.DS_Store -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/MultiplicationApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MultiplicationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(MultiplicationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/challenge/Challenge.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * This class represents a Challenge to solve a Multiplication (a * b). 7 | */ 8 | @Getter 9 | @ToString 10 | @EqualsAndHashCode 11 | @AllArgsConstructor 12 | public class Challenge { 13 | private int factorA; 14 | private int factorB; 15 | } 16 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttempt.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | import microservices.book.multiplication.user.User; 5 | 6 | /** 7 | * Identifies the attempt from a {@link User} to solve a challenge. 8 | */ 9 | @Getter 10 | @ToString 11 | @EqualsAndHashCode 12 | @AllArgsConstructor 13 | public class ChallengeAttempt { 14 | private Long id; 15 | private User user; 16 | private int factorA; 17 | private int factorB; 18 | private int resultAttempt; 19 | private boolean correct; 20 | } 21 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import javax.validation.Valid; 9 | 10 | /** 11 | * This class provides a REST API to POST the attempts from users. 12 | */ 13 | @Slf4j 14 | @RequiredArgsConstructor 15 | @RestController 16 | @RequestMapping("/attempts") 17 | class ChallengeAttemptController { 18 | 19 | private final ChallengeService challengeService; 20 | 21 | @PostMapping 22 | ResponseEntity postResult( 23 | @RequestBody @Valid ChallengeAttemptDTO challengeAttemptDTO) { 24 | return ResponseEntity.ok(challengeService.verifyAttempt(challengeAttemptDTO)); 25 | } 26 | } -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.Value; 4 | 5 | import javax.validation.constraints.*; 6 | 7 | /** 8 | * Attempt coming from the user 9 | */ 10 | @Value 11 | public class ChallengeAttemptDTO { 12 | 13 | @Min(1) @Max(99) 14 | int factorA, factorB; 15 | @NotBlank 16 | String userAlias; 17 | @Positive(message = "How could you possibly get a negative result here? Try again.") 18 | int guess; 19 | 20 | } -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | /** 8 | * This class implements a REST API to get random challenges 9 | */ 10 | @Slf4j 11 | @RequiredArgsConstructor 12 | @RestController 13 | @RequestMapping("/challenges") 14 | public class ChallengeController { 15 | 16 | private final ChallengeGeneratorService challengeGeneratorService; 17 | 18 | @GetMapping("/random") 19 | Challenge getRandomChallenge() { 20 | Challenge challenge = challengeGeneratorService.randomChallenge(); 21 | log.info("Generating a random challenge: {}", challenge); 22 | return challenge; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | public interface ChallengeGeneratorService { 4 | 5 | /** 6 | * @return a randomly-generated challenge with factors between 11 and 99 7 | */ 8 | Challenge randomChallenge(); 9 | 10 | } -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorServiceImpl.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.util.Random; 6 | 7 | @Service 8 | public class ChallengeGeneratorServiceImpl implements ChallengeGeneratorService { 9 | 10 | private final static int MINIMUM_FACTOR = 11; 11 | private final static int MAXIMUM_FACTOR = 100; 12 | 13 | private final Random random; 14 | 15 | ChallengeGeneratorServiceImpl() { 16 | this.random = new Random(); 17 | } 18 | 19 | protected ChallengeGeneratorServiceImpl(final Random random) { 20 | this.random = random; 21 | } 22 | 23 | private int next() { 24 | return random.nextInt(MAXIMUM_FACTOR - MINIMUM_FACTOR) + MINIMUM_FACTOR; 25 | } 26 | 27 | @Override 28 | public Challenge randomChallenge() { 29 | return new Challenge(next(), next()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | public interface ChallengeService { 4 | 5 | /** 6 | * Verifies if an attempt coming from the presentation layer is correct or not. 7 | * 8 | * @return the resulting ChallengeAttempt object 9 | */ 10 | ChallengeAttempt verifyAttempt(ChallengeAttemptDTO attemptDTO); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import microservices.book.multiplication.user.User; 4 | import org.springframework.stereotype.Service; 5 | 6 | @Service 7 | public class ChallengeServiceImpl implements ChallengeService { 8 | 9 | @Override 10 | public ChallengeAttempt verifyAttempt(ChallengeAttemptDTO attemptDTO) { 11 | // Check if the attempt is correct 12 | boolean isCorrect = attemptDTO.getGuess() == 13 | attemptDTO.getFactorA() * attemptDTO.getFactorB(); 14 | 15 | // We don't use identifiers for now 16 | User user = new User(null, attemptDTO.getUserAlias()); 17 | 18 | // Builds the domain object. Null id for now. 19 | ChallengeAttempt checkedAttempt = new ChallengeAttempt(null, 20 | user, 21 | attemptDTO.getFactorA(), 22 | attemptDTO.getFactorB(), 23 | attemptDTO.getGuess(), 24 | isCorrect 25 | ); 26 | 27 | return checkedAttempt; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/java/microservices/book/multiplication/user/User.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * Stores information to identify the user. 7 | */ 8 | @Getter 9 | @ToString 10 | @EqualsAndHashCode 11 | @AllArgsConstructor 12 | public class User { 13 | 14 | private Long id; 15 | private String alias; 16 | 17 | public User(final String userAlias) { 18 | this(null, userAlias); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter03-master/multiplication/src/main/resources/.DS_Store -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.error.include-message=always 2 | server.error.include-binding-errors=always 3 | -------------------------------------------------------------------------------- /chapter03-master/multiplication/src/test/java/microservices/book/multiplication/MultiplicationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class MultiplicationApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter03-master/resources/app-layers-chapter3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter03-master/resources/app-layers-chapter3.png -------------------------------------------------------------------------------- /chapter03-master/resources/business_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter03-master/resources/business_model.png -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "challenges-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-scripts": "3.4.1" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/challenges-frontend/public/favicon.ico -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/challenges-frontend/public/logo192.png -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/challenges-frontend/public/logo512.png -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.css'; 3 | import ChallengeComponent from './components/ChallengeComponent'; 4 | 5 | function App() { 6 | return ( 7 |
8 |
9 | 10 |
11 |
12 | ); 13 | } 14 | 15 | export default App; -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://bit.ly/CRA-PWA 17 | serviceWorker.unregister(); 18 | -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/src/services/ApiClient.js: -------------------------------------------------------------------------------- 1 | class ApiClient { 2 | 3 | static SERVER_URL = 'http://localhost:8080'; 4 | static GET_CHALLENGE = '/challenges/random'; 5 | static POST_RESULT = '/attempts'; 6 | 7 | static challenge(): Promise { 8 | return fetch(ApiClient.SERVER_URL + ApiClient.GET_CHALLENGE); 9 | } 10 | 11 | static sendGuess(user: string, 12 | a: number, 13 | b: number, 14 | guess: number): Promise { 15 | return fetch(ApiClient.SERVER_URL + ApiClient.POST_RESULT, 16 | { 17 | method: 'POST', 18 | headers: { 19 | 'Content-Type': 'application/json' 20 | }, 21 | body: JSON.stringify( 22 | { 23 | userAlias: user, 24 | factorA: a, 25 | factorB: b, 26 | guess: guess 27 | } 28 | ) 29 | }); 30 | } 31 | } 32 | 33 | export default ApiClient; 34 | -------------------------------------------------------------------------------- /chapter04-master/challenges-frontend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/multiplication/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter04-master/multiplication/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/multiplication/src/.DS_Store -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/multiplication/src/main/.DS_Store -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/multiplication/src/main/java/.DS_Store -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/multiplication/src/main/java/microservices/.DS_Store -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/multiplication/src/main/java/microservices/book/.DS_Store -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/MultiplicationApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MultiplicationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(MultiplicationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/challenge/Challenge.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * This class represents a Challenge to solve a Multiplication (a * b). 7 | */ 8 | @Getter 9 | @ToString 10 | @EqualsAndHashCode 11 | @AllArgsConstructor 12 | public class Challenge { 13 | private int factorA; 14 | private int factorB; 15 | } 16 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttempt.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | import microservices.book.multiplication.user.User; 5 | 6 | /** 7 | * Identifies the attempt from a {@link User} to solve a challenge. 8 | */ 9 | @Getter 10 | @ToString 11 | @EqualsAndHashCode 12 | @AllArgsConstructor 13 | public class ChallengeAttempt { 14 | private Long id; 15 | private User user; 16 | private int factorA; 17 | private int factorB; 18 | private int resultAttempt; 19 | private boolean correct; 20 | } 21 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import javax.validation.Valid; 9 | 10 | /** 11 | * This class provides a REST API to POST the attempts from users. 12 | */ 13 | @Slf4j 14 | @RequiredArgsConstructor 15 | @RestController 16 | @RequestMapping("/attempts") 17 | class ChallengeAttemptController { 18 | 19 | private final ChallengeService challengeService; 20 | 21 | @PostMapping 22 | ResponseEntity postResult( 23 | @RequestBody @Valid ChallengeAttemptDTO challengeAttemptDTO) { 24 | return ResponseEntity.ok(challengeService.verifyAttempt(challengeAttemptDTO)); 25 | } 26 | } -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.Value; 4 | 5 | import javax.validation.constraints.*; 6 | 7 | /** 8 | * Attempt coming from the user 9 | */ 10 | @Value 11 | public class ChallengeAttemptDTO { 12 | 13 | @Min(1) @Max(99) 14 | int factorA, factorB; 15 | @NotBlank 16 | String userAlias; 17 | @Positive(message = "How could you possibly get a negative result here? Try again.") 18 | int guess; 19 | 20 | } -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | /** 8 | * This class implements a REST API to get random challenges 9 | */ 10 | @Slf4j 11 | @RequiredArgsConstructor 12 | @RestController 13 | @RequestMapping("/challenges") 14 | public class ChallengeController { 15 | 16 | private final ChallengeGeneratorService challengeGeneratorService; 17 | 18 | @GetMapping("/random") 19 | Challenge getRandomChallenge() { 20 | Challenge challenge = challengeGeneratorService.randomChallenge(); 21 | log.info("Generating a random challenge: {}", challenge); 22 | return challenge; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | public interface ChallengeGeneratorService { 4 | 5 | /** 6 | * @return a randomly-generated challenge with factors between 11 and 99 7 | */ 8 | Challenge randomChallenge(); 9 | 10 | } -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorServiceImpl.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.util.Random; 6 | 7 | @Service 8 | public class ChallengeGeneratorServiceImpl implements ChallengeGeneratorService { 9 | 10 | private final static int MINIMUM_FACTOR = 11; 11 | private final static int MAXIMUM_FACTOR = 100; 12 | 13 | private final Random random; 14 | 15 | ChallengeGeneratorServiceImpl() { 16 | this.random = new Random(); 17 | } 18 | 19 | protected ChallengeGeneratorServiceImpl(final Random random) { 20 | this.random = random; 21 | } 22 | 23 | private int next() { 24 | return random.nextInt(MAXIMUM_FACTOR - MINIMUM_FACTOR) + MINIMUM_FACTOR; 25 | } 26 | 27 | @Override 28 | public Challenge randomChallenge() { 29 | return new Challenge(next(), next()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | public interface ChallengeService { 4 | 5 | /** 6 | * Verifies if an attempt coming from the presentation layer is correct or not. 7 | * 8 | * @return the resulting ChallengeAttempt object 9 | */ 10 | ChallengeAttempt verifyAttempt(ChallengeAttemptDTO attemptDTO); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import microservices.book.multiplication.user.User; 4 | import org.springframework.stereotype.Service; 5 | 6 | @Service 7 | public class ChallengeServiceImpl implements ChallengeService { 8 | 9 | @Override 10 | public ChallengeAttempt verifyAttempt(ChallengeAttemptDTO attemptDTO) { 11 | // Check if the attempt is correct 12 | boolean isCorrect = attemptDTO.getGuess() == 13 | attemptDTO.getFactorA() * attemptDTO.getFactorB(); 14 | 15 | // We don't use identifiers for now 16 | User user = new User(null, attemptDTO.getUserAlias()); 17 | 18 | // Builds the domain object. Null id for now. 19 | ChallengeAttempt checkedAttempt = new ChallengeAttempt(null, 20 | user, 21 | attemptDTO.getFactorA(), 22 | attemptDTO.getFactorB(), 23 | attemptDTO.getGuess(), 24 | isCorrect 25 | ); 26 | 27 | return checkedAttempt; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/configuration/WebConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfiguration implements WebMvcConfigurer { 9 | 10 | /** 11 | * Enables Cross-Origin Resource Sharing (CORS) 12 | * More info: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html 13 | */ 14 | @Override 15 | public void addCorsMappings(final CorsRegistry registry) { 16 | registry.addMapping("/**").allowedOrigins("http://localhost:3000"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/java/microservices/book/multiplication/user/User.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * Stores information to identify the user. 7 | */ 8 | @Getter 9 | @ToString 10 | @EqualsAndHashCode 11 | @AllArgsConstructor 12 | public class User { 13 | 14 | private Long id; 15 | private String alias; 16 | 17 | public User(final String userAlias) { 18 | this(null, userAlias); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/multiplication/src/main/resources/.DS_Store -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.error.include-message=always 2 | server.error.include-binding-errors=always 3 | -------------------------------------------------------------------------------- /chapter04-master/multiplication/src/test/java/microservices/book/multiplication/MultiplicationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class MultiplicationApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter04-master/resources/app-layers-chapter4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/resources/app-layers-chapter4.png -------------------------------------------------------------------------------- /chapter04-master/resources/chapter4_app_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter04-master/resources/chapter4_app_screenshot.png -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "challenges-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-scripts": "3.4.1" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter05-master/challenges-frontend/public/favicon.ico -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter05-master/challenges-frontend/public/logo192.png -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter05-master/challenges-frontend/public/logo512.png -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .display-column { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .challenge { 8 | font-size: 4em; 9 | } 10 | 11 | .badge { 12 | font-size: x-small; 13 | border: 2px solid dodgerblue; 14 | border-radius: 4px; 15 | padding: 0.2em; 16 | margin: 0.1em; 17 | } 18 | 19 | th { 20 | padding-right: 0.5em; 21 | border-bottom: solid 1px; 22 | } -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.css'; 3 | import ChallengeComponent from './components/ChallengeComponent'; 4 | 5 | function App() { 6 | return ; 7 | } 8 | 9 | export default App; 10 | -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/src/components/LastAttemptsComponent.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | class LastAttemptsComponent extends React.Component { 4 | 5 | render() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {this.props.lastAttempts.map(a => 17 | 19 | 20 | 21 | 23 | 24 | )} 25 | 26 |
ChallengeYour guessCorrect
{a.factorA} x {a.factorB}{a.resultAttempt}{a.correct ? "Correct" : 22 | ("Incorrect (" + a.factorA * a.factorB + ")")}
27 | ); 28 | } 29 | } 30 | 31 | export default LastAttemptsComponent; 32 | -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Segoe UI', Roboto, Arial, sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://bit.ly/CRA-PWA 17 | serviceWorker.unregister(); 18 | -------------------------------------------------------------------------------- /chapter05-master/challenges-frontend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter05-master/multiplication/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter05-master/multiplication/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/MultiplicationApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MultiplicationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(MultiplicationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/challenge/Challenge.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * This class represents a Challenge to solve a Multiplication (a * b). 7 | */ 8 | @Getter 9 | @ToString 10 | @EqualsAndHashCode 11 | @AllArgsConstructor 12 | public class Challenge { 13 | private final int factorA; 14 | private final int factorB; 15 | } 16 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttempt.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | import microservices.book.multiplication.user.User; 5 | 6 | import javax.persistence.*; 7 | 8 | /** 9 | * Identifies the attempt from a {@link User} to solve a challenge. 10 | */ 11 | @Entity 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class ChallengeAttempt { 16 | @Id 17 | @GeneratedValue 18 | private Long id; 19 | @ManyToOne(fetch = FetchType.LAZY) 20 | @JoinColumn(name = "USER_ID") 21 | private User user; 22 | private int factorA; 23 | private int factorB; 24 | private int resultAttempt; 25 | private boolean correct; 26 | } 27 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import javax.validation.Valid; 9 | import java.util.List; 10 | 11 | /** 12 | * This class provides a REST API to POST the attempts from users. 13 | */ 14 | @Slf4j 15 | @RequiredArgsConstructor 16 | @RestController 17 | @RequestMapping("/attempts") 18 | class ChallengeAttemptController { 19 | 20 | private final ChallengeService challengeService; 21 | 22 | @PostMapping 23 | ResponseEntity postResult( 24 | @RequestBody @Valid ChallengeAttemptDTO challengeAttemptDTO) { 25 | return ResponseEntity.ok(challengeService.verifyAttempt(challengeAttemptDTO)); 26 | } 27 | 28 | @GetMapping 29 | ResponseEntity> getStatistics(@RequestParam("alias") String alias) { 30 | return ResponseEntity.ok( 31 | challengeService.getStatsForUser(alias) 32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.Value; 4 | 5 | import javax.validation.constraints.*; 6 | 7 | /** 8 | * Attempt coming from the user 9 | */ 10 | @Value 11 | public class ChallengeAttemptDTO { 12 | 13 | @Min(1) @Max(99) 14 | int factorA, factorB; 15 | @NotBlank 16 | String userAlias; 17 | @Positive(message = "How could you possibly get a negative result here? Try again.") 18 | int guess; 19 | 20 | } -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | import java.util.List; 6 | 7 | public interface ChallengeAttemptRepository extends CrudRepository { 8 | 9 | /** 10 | * @return the last 10 attempts for a given user, identified by their alias. 11 | */ 12 | List findTop10ByUserAliasOrderByIdDesc(String userAlias); 13 | } 14 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | /** 8 | * This class implements a REST API to get random challenges 9 | */ 10 | @Slf4j 11 | @RequiredArgsConstructor 12 | @RestController 13 | @RequestMapping("/challenges") 14 | public class ChallengeController { 15 | 16 | private final ChallengeGeneratorService challengeGeneratorService; 17 | 18 | @GetMapping("/random") 19 | Challenge getRandomChallenge() { 20 | Challenge challenge = challengeGeneratorService.randomChallenge(); 21 | log.info("Generating a random challenge: {}", challenge); 22 | return challenge; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | public interface ChallengeGeneratorService { 4 | 5 | /** 6 | * @return a randomly-generated challenge with factors between 11 and 99 7 | */ 8 | Challenge randomChallenge(); 9 | 10 | } -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorServiceImpl.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.util.Random; 6 | 7 | @Service 8 | public class ChallengeGeneratorServiceImpl implements ChallengeGeneratorService { 9 | 10 | private final static int MINIMUM_FACTOR = 11; 11 | private final static int MAXIMUM_FACTOR = 100; 12 | 13 | private final Random random; 14 | 15 | ChallengeGeneratorServiceImpl() { 16 | this.random = new Random(); 17 | } 18 | 19 | protected ChallengeGeneratorServiceImpl(final Random random) { 20 | this.random = random; 21 | } 22 | 23 | private int next() { 24 | return random.nextInt(MAXIMUM_FACTOR - MINIMUM_FACTOR) + MINIMUM_FACTOR; 25 | } 26 | 27 | @Override 28 | public Challenge randomChallenge() { 29 | return new Challenge(next(), next()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import java.util.List; 4 | 5 | public interface ChallengeService { 6 | 7 | /** 8 | * Verifies if an attempt coming from the presentation layer is correct or not. 9 | * 10 | * @return the resulting ChallengeAttempt object 11 | */ 12 | ChallengeAttempt verifyAttempt(ChallengeAttemptDTO attemptDTO); 13 | 14 | /** 15 | * Gets the statistics for a given user. 16 | * 17 | * @param userAlias the user's alias 18 | * @return a list of the last 10 {@link ChallengeAttempt} 19 | * objects created by the user. 20 | */ 21 | List getStatsForUser(final String userAlias); 22 | } 23 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/configuration/JsonConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import com.fasterxml.jackson.databind.Module; 4 | import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class JsonConfiguration { 10 | 11 | @Bean 12 | public Module hibernateModule() { 13 | return new Hibernate5Module(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/configuration/WebConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfiguration implements WebMvcConfigurer { 9 | 10 | /** 11 | * Enables Cross-Origin Resource Sharing (CORS) 12 | * More info: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html 13 | */ 14 | @Override 15 | public void addCorsMappings(final CorsRegistry registry) { 16 | registry.addMapping("/**").allowedOrigins("http://localhost:3000"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/user/User.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * Stores information to identify the user. 9 | */ 10 | @Entity 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class User { 15 | 16 | @Id 17 | @GeneratedValue 18 | private Long id; 19 | private String alias; 20 | 21 | public User(final String userAlias) { 22 | this(null, userAlias); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/java/microservices/book/multiplication/user/UserRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | import java.util.Optional; 6 | 7 | public interface UserRepository extends CrudRepository { 8 | 9 | Optional findByAlias(final String alias); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Includes validation messages in responses 2 | server.error.include-message=always 3 | server.error.include-binding-errors=always 4 | # Gives us access to the H2 database web console 5 | spring.h2.console.enabled=true 6 | # Creates the database in a file 7 | spring.datasource.url=jdbc:h2:file:~/multiplication;DB_CLOSE_ON_EXIT=FALSE 8 | # Creates or updates the schema if needed 9 | spring.jpa.hibernate.ddl-auto=update 10 | # For educational purposes we will show the SQL in console 11 | spring.jpa.show-sql=true 12 | -------------------------------------------------------------------------------- /chapter05-master/multiplication/src/test/java/microservices/book/multiplication/MultiplicationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class MultiplicationApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter05-master/resources/app-last-attempts-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter05-master/resources/app-last-attempts-screenshot.png -------------------------------------------------------------------------------- /chapter05-master/resources/app-layers-chapter5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter05-master/resources/app-layers-chapter5.png -------------------------------------------------------------------------------- /chapter05-master/resources/data_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter05-master/resources/data_model.png -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "challenges-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-scripts": "3.4.1" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/challenges-frontend/public/favicon.ico -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/challenges-frontend/public/logo192.png -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/challenges-frontend/public/logo512.png -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .display-column { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .challenge { 8 | font-size: 4em; 9 | } 10 | 11 | th { 12 | padding-right: 0.5em; 13 | border-bottom: solid 1px; 14 | } 15 | 16 | .badge { 17 | font-size: x-small; 18 | border: 2px solid dodgerblue; 19 | border-radius: 4px; 20 | padding: 0.2em; 21 | margin: 0.1em; 22 | } 23 | -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.css'; 3 | import ChallengeComponent from './components/ChallengeComponent'; 4 | 5 | function App() { 6 | return ; 7 | } 8 | 9 | export default App; 10 | -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/src/components/LastAttemptsComponent.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | class LastAttemptsComponent extends React.Component { 4 | 5 | render() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {this.props.lastAttempts.map(a => 17 | 19 | 20 | 21 | 23 | 24 | )} 25 | 26 |
ChallengeYour guessCorrect
{a.factorA} x {a.factorB}{a.resultAttempt}{a.correct ? "Correct" : 22 | ("Incorrect (" + a.factorA * a.factorB + ")")}
27 | ); 28 | } 29 | } 30 | 31 | export default LastAttemptsComponent; 32 | -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Segoe UI', Roboto, Arial, sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://bit.ly/CRA-PWA 17 | serviceWorker.unregister(); 18 | -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/src/services/GameApiClient.js: -------------------------------------------------------------------------------- 1 | class GameApiClient { 2 | static SERVER_URL = 'http://localhost:8081'; 3 | static GET_LEADERBOARD = '/leaders'; 4 | 5 | static leaderBoard(): Promise { 6 | return fetch(GameApiClient.SERVER_URL + 7 | GameApiClient.GET_LEADERBOARD); 8 | } 9 | 10 | } 11 | 12 | export default GameApiClient; 13 | -------------------------------------------------------------------------------- /chapter06-master/challenges-frontend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /chapter06-master/gamification/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/gamification/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter06-master/gamification/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter06-master/gamification/game-layers.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | skinparam dpi 300 3 | scale 3 4 | skinparam class { 5 | BackgroundColor Ivory 6 | ArrowColor DarkGrey 7 | BorderColor Silver 8 | } 9 | 10 | interface LeaderBoardService 11 | 12 | LeaderBoardController "1" o-- "1" LeaderBoardService : uses > 13 | LeaderBoardServiceImpl .up.|> LeaderBoardService : implements 14 | LeaderBoardServiceImpl "1" o-down- "1" BadgeRepository : uses > 15 | LeaderBoardServiceImpl "1" o-down- "1" ScoreRepository : uses > 16 | @enduml 17 | -------------------------------------------------------------------------------- /chapter06-master/gamification/game_layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/gamification/game_layers.png -------------------------------------------------------------------------------- /chapter06-master/gamification/leaderboard-layers.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | skinparam dpi 300 3 | scale 3 4 | skinparam class { 5 | BackgroundColor Ivory 6 | ArrowColor DarkGrey 7 | BorderColor Silver 8 | } 9 | 10 | interface GameService 11 | interface BadgeProcessor 12 | 13 | GameController "1" o-- "1" GameService : uses > 14 | GameServiceImpl .|> GameService : implements 15 | GameServiceImpl "1" o-up- "*" BadgeProcessor : uses many 16 | GameServiceImpl "1" o-down- "1" BadgeRepository : uses > 17 | GameServiceImpl "1" o-down- "1" ScoreRepository : uses > 18 | @enduml 19 | -------------------------------------------------------------------------------- /chapter06-master/gamification/leaderboard_layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/gamification/leaderboard_layers.png -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/GamificationApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class GamificationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(GamificationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/challenge/ChallengeSolvedDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.challenge; 2 | 3 | import lombok.Value; 4 | 5 | @Value 6 | public class ChallengeSolvedDTO { 7 | 8 | long attemptId; 9 | boolean correct; 10 | int factorA; 11 | int factorB; 12 | long userId; 13 | String userAlias; 14 | 15 | } -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/configuration/JsonConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.configuration; 2 | 3 | import com.fasterxml.jackson.databind.Module; 4 | import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class JsonConfiguration { 10 | 11 | @Bean 12 | public Module hibernateModule() { 13 | return new Hibernate5Module(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/configuration/WebConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.configuration; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfiguration implements WebMvcConfigurer { 9 | 10 | /** 11 | * Enables Cross-Origin Resource Sharing (CORS) 12 | * More info: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html 13 | */ 14 | @Override 15 | public void addCorsMappings(final CorsRegistry registry) { 16 | registry.addMapping("/**").allowedOrigins("http://localhost:3000"); 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/BadgeRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import microservices.book.gamification.game.domain.BadgeCard; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import org.springframework.data.repository.CrudRepository; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Handles data operations with BadgeCards 11 | */ 12 | public interface BadgeRepository extends CrudRepository { 13 | 14 | /** 15 | * Retrieves all BadgeCards for a given user. 16 | * 17 | * @param userId the id of the user to look for BadgeCards 18 | * @return the list of BadgeCards, sorted by most recent. 19 | */ 20 | List findByUserIdOrderByBadgeTimestampDesc(final Long userId); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/GameController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.*; 5 | 6 | import lombok.RequiredArgsConstructor; 7 | import microservices.book.gamification.challenge.ChallengeSolvedDTO; 8 | 9 | @RestController 10 | @RequestMapping("/attempts") 11 | @RequiredArgsConstructor 12 | public class GameController { 13 | 14 | private final GameService gameService; 15 | 16 | @PostMapping 17 | @ResponseStatus(HttpStatus.OK) 18 | void postResult(@RequestBody ChallengeSolvedDTO dto) { 19 | gameService.newAttemptForUser(dto); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/GameService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import java.util.List; 4 | 5 | import lombok.Value; 6 | import microservices.book.gamification.challenge.ChallengeSolvedDTO; 7 | import microservices.book.gamification.game.domain.BadgeType; 8 | 9 | /** 10 | * This service includes the main logic for gamifying the system. 11 | */ 12 | public interface GameService { 13 | 14 | /** 15 | * Process a new attempt from a given user. 16 | * 17 | * @param challenge the challenge data with user details, factors, etc. 18 | * @return a {@link GameResult} object containing the new score and badge cards obtained 19 | */ 20 | GameResult newAttemptForUser(ChallengeSolvedDTO challenge); 21 | 22 | @Value 23 | class GameResult { 24 | int score; 25 | List badges; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/LeaderBoardController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import microservices.book.gamification.game.domain.LeaderBoardRow; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * This class implements a REST API for the Gamification LeaderBoard service. 11 | */ 12 | @RestController 13 | @RequestMapping("/leaders") 14 | @RequiredArgsConstructor 15 | class LeaderBoardController { 16 | 17 | private final LeaderBoardService leaderBoardService; 18 | 19 | @GetMapping 20 | public List getLeaderBoard() { 21 | return leaderBoardService.getCurrentLeaderBoard(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/LeaderBoardService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import java.util.List; 4 | 5 | import microservices.book.gamification.game.domain.LeaderBoardRow; 6 | 7 | public interface LeaderBoardService { 8 | 9 | /** 10 | * @return the current leader board ranked from high to low score 11 | */ 12 | List getCurrentLeaderBoard(); 13 | } 14 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/BadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | import microservices.book.gamification.challenge.ChallengeSolvedDTO; 7 | import microservices.book.gamification.game.domain.BadgeType; 8 | import microservices.book.gamification.game.domain.ScoreCard; 9 | 10 | public interface BadgeProcessor { 11 | 12 | /** 13 | * Processes some or all of the passed parameters and decides if the user 14 | * is entitled to a badge. 15 | * 16 | * @return a BadgeType if the user is entitled to this badge, otherwise empty 17 | */ 18 | Optional processForOptionalBadge(int currentScore, 19 | List scoreCardList, 20 | ChallengeSolvedDTO solved); 21 | 22 | /** 23 | * @return the BadgeType object that this processor is handling. You can use 24 | * it to filter processors according to your needs. 25 | */ 26 | BadgeType badgeType(); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/BronzeBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedDTO; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class BronzeBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedDTO solved) { 18 | return currentScore > 50 ? 19 | Optional.of(BadgeType.BRONZE) : 20 | Optional.empty(); 21 | } 22 | 23 | @Override 24 | public BadgeType badgeType() { 25 | return BadgeType.BRONZE; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/FirstWonBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedDTO; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class FirstWonBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedDTO solved) { 18 | return scoreCardList.size() == 1 ? 19 | Optional.of(BadgeType.FIRST_WON) : Optional.empty(); 20 | } 21 | 22 | @Override 23 | public BadgeType badgeType() { 24 | return BadgeType.FIRST_WON; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/GoldBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedDTO; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class GoldBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedDTO solved) { 18 | return currentScore > 400 ? 19 | Optional.of(BadgeType.GOLD) : 20 | Optional.empty(); 21 | } 22 | 23 | @Override 24 | public BadgeType badgeType() { 25 | return BadgeType.GOLD; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/SilverBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedDTO; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class SilverBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedDTO solved) { 18 | return currentScore > 150 ? 19 | Optional.of(BadgeType.SILVER) : 20 | Optional.empty(); 21 | } 22 | 23 | @Override 24 | public BadgeType badgeType() { 25 | return BadgeType.SILVER; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/domain/BadgeCard.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * This class links a Badge to a User. Contains also a timestamp with the moment in which the user got it. 9 | */ 10 | @Entity 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class BadgeCard { 15 | 16 | @Id 17 | @GeneratedValue 18 | private Long badgeId; 19 | 20 | private Long userId; 21 | @EqualsAndHashCode.Exclude 22 | private long badgeTimestamp; 23 | private BadgeType badgeType; 24 | 25 | public BadgeCard(final Long userId, final BadgeType badgeType) { 26 | this(null, userId, System.currentTimeMillis(), badgeType); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/domain/BadgeType.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | /** 7 | * Enumeration with the different types of Badges that a user can win. 8 | */ 9 | @RequiredArgsConstructor 10 | @Getter 11 | public enum BadgeType { 12 | 13 | // Badges depending on score 14 | BRONZE("Bronze"), 15 | SILVER("Silver"), 16 | GOLD("Gold"), 17 | 18 | // Other badges won for different conditions 19 | FIRST_WON("First time"), 20 | LUCKY_NUMBER("Lucky number"); 21 | 22 | private final String description; 23 | } 24 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/domain/LeaderBoardRow.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.*; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Represents a line in our Leaderboard. 9 | */ 10 | @Value 11 | @AllArgsConstructor 12 | public class LeaderBoardRow { 13 | 14 | Long userId; 15 | Long totalScore; 16 | @With 17 | List badges; 18 | 19 | public LeaderBoardRow(final Long userId, final Long totalScore) { 20 | this.userId = userId; 21 | this.totalScore = totalScore; 22 | this.badges = List.of(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/java/microservices/book/gamification/game/domain/ScoreCard.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * This class represents the Score linked to an attempt in the game, 9 | * with an associated user and the timestamp in which the score 10 | * is registered. 11 | */ 12 | @Entity 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class ScoreCard { 17 | 18 | // The default score assigned to this card, if not specified. 19 | public static final int DEFAULT_SCORE = 10; 20 | 21 | @Id 22 | @GeneratedValue 23 | private Long cardId; 24 | private Long userId; 25 | private Long attemptId; 26 | @EqualsAndHashCode.Exclude 27 | private long scoreTimestamp; 28 | private int score; 29 | 30 | public ScoreCard(final Long userId, final Long attemptId) { 31 | this(null, userId, attemptId, System.currentTimeMillis(), DEFAULT_SCORE); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8081 2 | # Gives us access to the H2 database web console 3 | spring.h2.console.enabled=true 4 | # Creates the database in a file 5 | spring.datasource.url=jdbc:h2:file:~/gamification;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=TRUE 6 | # Creates or updates the schema if needed 7 | spring.jpa.hibernate.ddl-auto=update 8 | # For educational purposes we will show the SQL in console 9 | spring.jpa.show-sql=true 10 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/test/java/microservices/book/gamification/GamificationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class GamificationApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter06-master/gamification/src/test/java/microservices/book/gamification/game/badgeprocessors/BronzeBadgeProcessorTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.game.domain.BadgeType; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class BronzeBadgeProcessorTest { 13 | 14 | private BronzeBadgeProcessor badgeProcessor; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | badgeProcessor = new BronzeBadgeProcessor(); 19 | } 20 | 21 | @Test 22 | public void shouldGiveBadgeIfScoreOverThreshold() { 23 | Optional badgeType = badgeProcessor 24 | .processForOptionalBadge(60, List.of(), null); 25 | assertThat(badgeType).contains(BadgeType.BRONZE); 26 | } 27 | 28 | @Test 29 | public void shouldNotGiveBadgeIfScoreUnderThreshold() { 30 | Optional badgeType = badgeProcessor 31 | .processForOptionalBadge(40, List.of(), null); 32 | assertThat(badgeType).isEmpty(); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter06-master/gamification/src/test/java/microservices/book/gamification/game/badgeprocessors/GoldBadgeProcessorTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.game.domain.BadgeType; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class GoldBadgeProcessorTest { 13 | 14 | private GoldBadgeProcessor badgeProcessor; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | badgeProcessor = new GoldBadgeProcessor(); 19 | } 20 | 21 | @Test 22 | public void shouldGiveBadgeIfScoreOverThreshold() { 23 | Optional badgeType = badgeProcessor 24 | .processForOptionalBadge(450, List.of(), null); 25 | assertThat(badgeType).contains(BadgeType.GOLD); 26 | } 27 | 28 | @Test 29 | public void shouldNotGiveBadgeIfScoreUnderThreshold() { 30 | Optional badgeType = badgeProcessor 31 | .processForOptionalBadge(350, List.of(), null); 32 | assertThat(badgeType).isEmpty(); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter06-master/gamification/src/test/java/microservices/book/gamification/game/badgeprocessors/SilverBadgeProcessorTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.game.domain.BadgeType; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class SilverBadgeProcessorTest { 13 | 14 | private SilverBadgeProcessor badgeProcessor; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | badgeProcessor = new SilverBadgeProcessor(); 19 | } 20 | 21 | @Test 22 | public void shouldGiveBadgeIfScoreOverThreshold() { 23 | Optional badgeType = badgeProcessor 24 | .processForOptionalBadge(160, List.of(), null); 25 | assertThat(badgeType).contains(BadgeType.SILVER); 26 | } 27 | 28 | @Test 29 | public void shouldNotGiveBadgeIfScoreUnderThreshold() { 30 | Optional badgeType = badgeProcessor 31 | .processForOptionalBadge(140, List.of(), null); 32 | assertThat(badgeType).isEmpty(); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter06-master/multiplication/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/multiplication/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter06-master/multiplication/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/MultiplicationApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MultiplicationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(MultiplicationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/Challenge.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * This class represents a Challenge to solve a Multiplication (a * b). 7 | */ 8 | @Getter 9 | @ToString 10 | @EqualsAndHashCode 11 | @AllArgsConstructor 12 | public class Challenge { 13 | private final int factorA; 14 | private final int factorB; 15 | } 16 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttempt.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | import microservices.book.multiplication.user.User; 5 | 6 | import javax.persistence.*; 7 | 8 | /** 9 | * Identifies the attempt from a {@link User} to solve a challenge. 10 | */ 11 | @Entity 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class ChallengeAttempt { 16 | @Id 17 | @GeneratedValue 18 | private Long id; 19 | @ManyToOne(fetch = FetchType.LAZY) 20 | @JoinColumn(name = "USER_ID") 21 | private User user; 22 | private int factorA; 23 | private int factorB; 24 | private int resultAttempt; 25 | private boolean correct; 26 | } 27 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import javax.validation.Valid; 9 | import java.util.List; 10 | 11 | /** 12 | * This class provides a REST API to POST the attempts from users. 13 | */ 14 | @Slf4j 15 | @RequiredArgsConstructor 16 | @RestController 17 | @RequestMapping("/attempts") 18 | class ChallengeAttemptController { 19 | 20 | private final ChallengeService challengeService; 21 | 22 | @PostMapping 23 | ResponseEntity postResult( 24 | @RequestBody @Valid ChallengeAttemptDTO challengeAttemptDTO) { 25 | return ResponseEntity.ok(challengeService.verifyAttempt(challengeAttemptDTO)); 26 | } 27 | 28 | @GetMapping 29 | ResponseEntity> getStatistics(@RequestParam("alias") String alias) { 30 | return ResponseEntity.ok( 31 | challengeService.getStatsForUser(alias) 32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.Value; 4 | 5 | import javax.validation.constraints.*; 6 | 7 | /** 8 | * Attempt coming from the user 9 | */ 10 | @Value 11 | public class ChallengeAttemptDTO { 12 | 13 | @Min(1) @Max(99) 14 | int factorA, factorB; 15 | @NotBlank 16 | String userAlias; 17 | @Positive(message = "How could you possibly get a negative result here? Try again.") 18 | int guess; 19 | 20 | } -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | import java.util.List; 6 | 7 | public interface ChallengeAttemptRepository extends CrudRepository { 8 | 9 | /** 10 | * @return the last 10 attempts for a given user, identified by their alias. 11 | */ 12 | List findTop10ByUserAliasOrderByIdDesc(String userAlias); 13 | } 14 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | /** 8 | * This class implements a REST API to get random challenges 9 | */ 10 | @Slf4j 11 | @RequiredArgsConstructor 12 | @RestController 13 | @RequestMapping("/challenges") 14 | public class ChallengeController { 15 | 16 | private final ChallengeGeneratorService challengeGeneratorService; 17 | 18 | @GetMapping("/random") 19 | Challenge getRandomChallenge() { 20 | Challenge challenge = challengeGeneratorService.randomChallenge(); 21 | log.info("Generating a random challenge: {}", challenge); 22 | return challenge; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | public interface ChallengeGeneratorService { 4 | 5 | /** 6 | * @return a randomly-generated challenge with factors between 11 and 99 7 | */ 8 | Challenge randomChallenge(); 9 | 10 | } -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorServiceImpl.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.util.Random; 6 | 7 | @Service 8 | public class ChallengeGeneratorServiceImpl implements ChallengeGeneratorService { 9 | 10 | private final static int MINIMUM_FACTOR = 11; 11 | private final static int MAXIMUM_FACTOR = 100; 12 | 13 | private final Random random; 14 | 15 | ChallengeGeneratorServiceImpl() { 16 | this.random = new Random(); 17 | } 18 | 19 | protected ChallengeGeneratorServiceImpl(final Random random) { 20 | this.random = random; 21 | } 22 | 23 | private int next() { 24 | return random.nextInt(MAXIMUM_FACTOR - MINIMUM_FACTOR) + MINIMUM_FACTOR; 25 | } 26 | 27 | @Override 28 | public Challenge randomChallenge() { 29 | return new Challenge(next(), next()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import java.util.List; 4 | 5 | public interface ChallengeService { 6 | 7 | /** 8 | * Verifies if an attempt coming from the presentation layer is correct or not. 9 | * 10 | * @return the resulting ChallengeAttempt object 11 | */ 12 | ChallengeAttempt verifyAttempt(ChallengeAttemptDTO attemptDTO); 13 | 14 | /** 15 | * Gets the statistics for a given user. 16 | * 17 | * @param userAlias the user's alias 18 | * @return a list of the last 10 {@link ChallengeAttempt} 19 | * objects created by the user. 20 | */ 21 | List getStatsForUser(final String userAlias); 22 | } 23 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeSolvedDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.Value; 4 | 5 | @Value 6 | public class ChallengeSolvedDTO { 7 | 8 | long attemptId; 9 | boolean correct; 10 | int factorA; 11 | int factorB; 12 | long userId; 13 | String userAlias; 14 | 15 | } -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/configuration/JsonConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import com.fasterxml.jackson.databind.Module; 4 | import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class JsonConfiguration { 10 | 11 | @Bean 12 | public Module hibernateModule() { 13 | return new Hibernate5Module(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/configuration/WebConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfiguration implements WebMvcConfigurer { 9 | 10 | /** 11 | * Enables Cross-Origin Resource Sharing (CORS) 12 | * More info: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html 13 | */ 14 | @Override 15 | public void addCorsMappings(final CorsRegistry registry) { 16 | registry.addMapping("/**").allowedOrigins("http://localhost:3000"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/user/User.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * Stores information to identify the user. 9 | */ 10 | @Entity 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class User { 15 | 16 | @Id 17 | @GeneratedValue 18 | private Long id; 19 | private String alias; 20 | 21 | public User(final String userAlias) { 22 | this(null, userAlias); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/user/UserController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | @Slf4j 11 | @RequiredArgsConstructor 12 | @RestController 13 | @RequestMapping("/users") 14 | public class UserController { 15 | 16 | private final UserRepository userRepository; 17 | 18 | @GetMapping("/{idList}") 19 | public List getUsersByIdList(@PathVariable final List idList) { 20 | return userRepository.findAllByIdIn(idList); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/java/microservices/book/multiplication/user/UserRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | import org.springframework.data.repository.CrudRepository; 7 | 8 | public interface UserRepository extends CrudRepository { 9 | 10 | Optional findByAlias(final String alias); 11 | 12 | List findAllByIdIn(final List ids); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Includes validation messages in responses 2 | server.error.include-message=always 3 | server.error.include-binding-errors=always 4 | # Gives us access to the H2 database web console 5 | spring.h2.console.enabled=true 6 | # Creates the database in a file 7 | spring.datasource.url=jdbc:h2:file:~/multiplication;DB_CLOSE_ON_EXIT=FALSE 8 | # Creates or updates the schema if needed 9 | spring.jpa.hibernate.ddl-auto=update 10 | # For educational purposes we will show the SQL in console 11 | spring.jpa.show-sql=true 12 | 13 | # Gamification service URL 14 | service.gamification.host=http://localhost:8081 15 | -------------------------------------------------------------------------------- /chapter06-master/multiplication/src/test/java/microservices/book/multiplication/MultiplicationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class MultiplicationApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter06-master/resources/app-screenshot-leaderboard-1b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/resources/app-screenshot-leaderboard-1b.png -------------------------------------------------------------------------------- /chapter06-master/resources/business_model-Chapter6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/resources/business_model-Chapter6.png -------------------------------------------------------------------------------- /chapter06-master/resources/logical_views-Chapter6c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter06-master/resources/logical_views-Chapter6c.png -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "challenges-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-scripts": "3.4.1" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter07-master/challenges-frontend/public/favicon.ico -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter07-master/challenges-frontend/public/logo192.png -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter07-master/challenges-frontend/public/logo512.png -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .display-column { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .challenge { 8 | font-size: 4em; 9 | } 10 | 11 | th { 12 | padding-right: 0.5em; 13 | border-bottom: solid 1px; 14 | } 15 | 16 | .badge { 17 | font-size: x-small; 18 | border: 2px solid dodgerblue; 19 | border-radius: 4px; 20 | padding: 0.2em; 21 | margin: 0.1em; 22 | } 23 | -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.css'; 3 | import ChallengeComponent from './components/ChallengeComponent'; 4 | 5 | function App() { 6 | return ; 7 | } 8 | 9 | export default App; 10 | -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/src/components/LastAttemptsComponent.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | class LastAttemptsComponent extends React.Component { 4 | 5 | render() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {this.props.lastAttempts.map(a => 17 | 19 | 20 | 21 | 23 | 24 | )} 25 | 26 |
ChallengeYour guessCorrect
{a.factorA} x {a.factorB}{a.resultAttempt}{a.correct ? "Correct" : 22 | ("Incorrect (" + a.factorA * a.factorB + ")")}
27 | ); 28 | } 29 | } 30 | 31 | export default LastAttemptsComponent; 32 | -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Segoe UI', Roboto, Arial, sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://bit.ly/CRA-PWA 17 | serviceWorker.unregister(); 18 | -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/src/services/GameApiClient.js: -------------------------------------------------------------------------------- 1 | class GameApiClient { 2 | static SERVER_URL = 'http://localhost:8081'; 3 | static GET_LEADERBOARD = '/leaders'; 4 | 5 | static leaderBoard(): Promise { 6 | return fetch(GameApiClient.SERVER_URL + 7 | GameApiClient.GET_LEADERBOARD); 8 | } 9 | 10 | } 11 | 12 | export default GameApiClient; 13 | -------------------------------------------------------------------------------- /chapter07-master/challenges-frontend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /chapter07-master/gamification/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter07-master/gamification/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter07-master/gamification/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter07-master/gamification/game-layers.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | skinparam dpi 300 3 | scale 3 4 | skinparam class { 5 | BackgroundColor Ivory 6 | ArrowColor DarkGrey 7 | BorderColor Silver 8 | } 9 | 10 | interface LeaderBoardService 11 | 12 | LeaderBoardController "1" o-- "1" LeaderBoardService : uses > 13 | LeaderBoardServiceImpl .up.|> LeaderBoardService : implements 14 | LeaderBoardServiceImpl "1" o-down- "1" BadgeRepository : uses > 15 | LeaderBoardServiceImpl "1" o-down- "1" ScoreRepository : uses > 16 | @enduml 17 | -------------------------------------------------------------------------------- /chapter07-master/gamification/game_layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter07-master/gamification/game_layers.png -------------------------------------------------------------------------------- /chapter07-master/gamification/leaderboard-layers.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | skinparam dpi 300 3 | scale 3 4 | skinparam class { 5 | BackgroundColor Ivory 6 | ArrowColor DarkGrey 7 | BorderColor Silver 8 | } 9 | 10 | interface GameService 11 | interface BadgeProcessor 12 | 13 | GameController "1" o-- "1" GameService : uses > 14 | GameServiceImpl .|> GameService : implements 15 | GameServiceImpl "1" o-up- "*" BadgeProcessor : uses many 16 | GameServiceImpl "1" o-down- "1" BadgeRepository : uses > 17 | GameServiceImpl "1" o-down- "1" ScoreRepository : uses > 18 | @enduml 19 | -------------------------------------------------------------------------------- /chapter07-master/gamification/leaderboard_layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter07-master/gamification/leaderboard_layers.png -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/GamificationApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class GamificationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(GamificationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/challenge/ChallengeSolvedEvent.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.challenge; 2 | 3 | import lombok.Value; 4 | 5 | @Value 6 | public class ChallengeSolvedEvent { 7 | 8 | long attemptId; 9 | boolean correct; 10 | int factorA; 11 | int factorB; 12 | long userId; 13 | String userAlias; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/configuration/JsonConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.configuration; 2 | 3 | import com.fasterxml.jackson.databind.Module; 4 | import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class JsonConfiguration { 10 | 11 | @Bean 12 | public Module hibernateModule() { 13 | return new Hibernate5Module(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/configuration/WebConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.configuration; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfiguration implements WebMvcConfigurer { 9 | 10 | /** 11 | * Enables Cross-Origin Resource Sharing (CORS) 12 | * More info: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html 13 | */ 14 | @Override 15 | public void addCorsMappings(final CorsRegistry registry) { 16 | registry.addMapping("/**").allowedOrigins("http://localhost:3000"); 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/BadgeRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import microservices.book.gamification.game.domain.BadgeCard; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import org.springframework.data.repository.CrudRepository; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Handles data operations with BadgeCards 11 | */ 12 | public interface BadgeRepository extends CrudRepository { 13 | 14 | /** 15 | * Retrieves all BadgeCards for a given user. 16 | * 17 | * @param userId the id of the user to look for BadgeCards 18 | * @return the list of BadgeCards, sorted by most recent. 19 | */ 20 | List findByUserIdOrderByBadgeTimestampDesc(final Long userId); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/GameEventHandler.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import org.springframework.amqp.AmqpRejectAndDontRequeueException; 4 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 5 | import org.springframework.stereotype.Service; 6 | 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 10 | 11 | @RequiredArgsConstructor 12 | @Slf4j 13 | @Service 14 | public class GameEventHandler { 15 | 16 | private final GameService gameService; 17 | 18 | @RabbitListener(queues = "${amqp.queue.gamification}") 19 | void handleMultiplicationSolved(final ChallengeSolvedEvent event) { 20 | log.info("Challenge Solved Event received: {}", event.getAttemptId()); 21 | try { 22 | gameService.newAttemptForUser(event); 23 | } catch (final Exception e) { 24 | log.error("Error when trying to process ChallengeSolvedEvent", e); 25 | // Avoids the event to be re-queued and reprocessed. 26 | throw new AmqpRejectAndDontRequeueException(e); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/GameService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import java.util.List; 4 | 5 | import lombok.Value; 6 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 7 | import microservices.book.gamification.game.domain.BadgeType; 8 | 9 | /** 10 | * This service includes the main logic for gamifying the system. 11 | */ 12 | public interface GameService { 13 | 14 | /** 15 | * Process a new attempt from a given user. 16 | * 17 | * @param challenge the challenge data with user details, factors, etc. 18 | * @return a {@link GameResult} object containing the new score and badge cards obtained 19 | */ 20 | GameResult newAttemptForUser(ChallengeSolvedEvent challenge); 21 | 22 | @Value 23 | class GameResult { 24 | int score; 25 | List badges; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/LeaderBoardController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import microservices.book.gamification.game.domain.LeaderBoardRow; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * This class implements a REST API for the Gamification LeaderBoard service. 11 | */ 12 | @RestController 13 | @RequestMapping("/leaders") 14 | @RequiredArgsConstructor 15 | class LeaderBoardController { 16 | 17 | private final LeaderBoardService leaderBoardService; 18 | 19 | @GetMapping 20 | public List getLeaderBoard() { 21 | return leaderBoardService.getCurrentLeaderBoard(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/LeaderBoardService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import java.util.List; 4 | 5 | import microservices.book.gamification.game.domain.LeaderBoardRow; 6 | 7 | public interface LeaderBoardService { 8 | 9 | /** 10 | * @return the current leader board ranked from high to low score 11 | */ 12 | List getCurrentLeaderBoard(); 13 | } 14 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/BadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 7 | import microservices.book.gamification.game.domain.BadgeType; 8 | import microservices.book.gamification.game.domain.ScoreCard; 9 | 10 | public interface BadgeProcessor { 11 | 12 | /** 13 | * Processes some or all of the passed parameters and decides if the user 14 | * is entitled to a badge. 15 | * 16 | * @return a BadgeType if the user is entitled to this badge, otherwise empty 17 | */ 18 | Optional processForOptionalBadge(int currentScore, 19 | List scoreCardList, 20 | ChallengeSolvedEvent solved); 21 | 22 | /** 23 | * @return the BadgeType object that this processor is handling. You can use 24 | * it to filter processors according to your needs. 25 | */ 26 | BadgeType badgeType(); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/BronzeBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class BronzeBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedEvent solved) { 18 | return currentScore > 50 ? 19 | Optional.of(BadgeType.BRONZE) : 20 | Optional.empty(); 21 | } 22 | 23 | @Override 24 | public BadgeType badgeType() { 25 | return BadgeType.BRONZE; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/FirstWonBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class FirstWonBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedEvent solved) { 18 | return scoreCardList.size() == 1 ? 19 | Optional.of(BadgeType.FIRST_WON) : Optional.empty(); 20 | } 21 | 22 | @Override 23 | public BadgeType badgeType() { 24 | return BadgeType.FIRST_WON; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/GoldBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class GoldBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedEvent solved) { 18 | return currentScore > 400 ? 19 | Optional.of(BadgeType.GOLD) : 20 | Optional.empty(); 21 | } 22 | 23 | @Override 24 | public BadgeType badgeType() { 25 | return BadgeType.GOLD; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/SilverBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class SilverBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedEvent solved) { 18 | return currentScore > 150 ? 19 | Optional.of(BadgeType.SILVER) : 20 | Optional.empty(); 21 | } 22 | 23 | @Override 24 | public BadgeType badgeType() { 25 | return BadgeType.SILVER; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/domain/BadgeCard.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * This class links a Badge to a User. Contains also a timestamp with the moment in which the user got it. 9 | */ 10 | @Entity 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class BadgeCard { 15 | 16 | @Id 17 | @GeneratedValue 18 | private Long badgeId; 19 | 20 | private Long userId; 21 | @EqualsAndHashCode.Exclude 22 | private long badgeTimestamp; 23 | private BadgeType badgeType; 24 | 25 | public BadgeCard(final Long userId, final BadgeType badgeType) { 26 | this(null, userId, System.currentTimeMillis(), badgeType); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/domain/BadgeType.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | /** 7 | * Enumeration with the different types of Badges that a user can win. 8 | */ 9 | @RequiredArgsConstructor 10 | @Getter 11 | public enum BadgeType { 12 | 13 | // Badges depending on score 14 | BRONZE("Bronze"), 15 | SILVER("Silver"), 16 | GOLD("Gold"), 17 | 18 | // Other badges won for different conditions 19 | FIRST_WON("First time"), 20 | LUCKY_NUMBER("Lucky number"); 21 | 22 | private final String description; 23 | } 24 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/domain/LeaderBoardRow.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.*; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Represents a line in our Leaderboard. 9 | */ 10 | @Value 11 | @AllArgsConstructor 12 | public class LeaderBoardRow { 13 | 14 | Long userId; 15 | Long totalScore; 16 | @With 17 | List badges; 18 | 19 | public LeaderBoardRow(final Long userId, final Long totalScore) { 20 | this.userId = userId; 21 | this.totalScore = totalScore; 22 | this.badges = List.of(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/java/microservices/book/gamification/game/domain/ScoreCard.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * This class represents the Score linked to an attempt in the game, 9 | * with an associated user and the timestamp in which the score 10 | * is registered. 11 | */ 12 | @Entity 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class ScoreCard { 17 | 18 | // The default score assigned to this card, if not specified. 19 | public static final int DEFAULT_SCORE = 10; 20 | 21 | @Id 22 | @GeneratedValue 23 | private Long cardId; 24 | private Long userId; 25 | private Long attemptId; 26 | @EqualsAndHashCode.Exclude 27 | private long scoreTimestamp; 28 | private int score; 29 | 30 | public ScoreCard(final Long userId, final Long attemptId) { 31 | this(null, userId, attemptId, System.currentTimeMillis(), DEFAULT_SCORE); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8081 2 | # Gives us access to the H2 database web console 3 | spring.h2.console.enabled=true 4 | # Creates the database in a file 5 | spring.datasource.url=jdbc:h2:file:~/gamification;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=TRUE; 6 | # Creates or updates the schema if needed 7 | spring.jpa.hibernate.ddl-auto=update 8 | # For educational purposes we will show the SQL in console 9 | #spring.jpa.show-sql=true 10 | 11 | amqp.exchange.attempts=attempts.topic 12 | amqp.queue.gamification=gamification.queue 13 | 14 | # Shows declaration of exchanges, queues, bindings, etc. 15 | logging.level.org.springframework.amqp.rabbit.core.RabbitAdmin = DEBUG 16 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/test/java/microservices/book/gamification/GamificationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class GamificationApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter07-master/gamification/src/test/java/microservices/book/gamification/game/badgeprocessors/BronzeBadgeProcessorTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.game.domain.BadgeType; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class BronzeBadgeProcessorTest { 13 | 14 | private BronzeBadgeProcessor badgeProcessor; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | badgeProcessor = new BronzeBadgeProcessor(); 19 | } 20 | 21 | @Test 22 | public void shouldGiveBadgeIfScoreOverThreshold() { 23 | Optional badgeType = badgeProcessor 24 | .processForOptionalBadge(60, List.of(), null); 25 | assertThat(badgeType).contains(BadgeType.BRONZE); 26 | } 27 | 28 | @Test 29 | public void shouldNotGiveBadgeIfScoreUnderThreshold() { 30 | Optional badgeType = badgeProcessor 31 | .processForOptionalBadge(40, List.of(), null); 32 | assertThat(badgeType).isEmpty(); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter07-master/gamification/src/test/java/microservices/book/gamification/game/badgeprocessors/GoldBadgeProcessorTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.game.domain.BadgeType; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class GoldBadgeProcessorTest { 13 | 14 | private GoldBadgeProcessor badgeProcessor; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | badgeProcessor = new GoldBadgeProcessor(); 19 | } 20 | 21 | @Test 22 | public void shouldGiveBadgeIfScoreOverThreshold() { 23 | Optional badgeType = badgeProcessor 24 | .processForOptionalBadge(450, List.of(), null); 25 | assertThat(badgeType).contains(BadgeType.GOLD); 26 | } 27 | 28 | @Test 29 | public void shouldNotGiveBadgeIfScoreUnderThreshold() { 30 | Optional badgeType = badgeProcessor 31 | .processForOptionalBadge(350, List.of(), null); 32 | assertThat(badgeType).isEmpty(); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter07-master/gamification/src/test/java/microservices/book/gamification/game/badgeprocessors/SilverBadgeProcessorTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.game.domain.BadgeType; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class SilverBadgeProcessorTest { 13 | 14 | private SilverBadgeProcessor badgeProcessor; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | badgeProcessor = new SilverBadgeProcessor(); 19 | } 20 | 21 | @Test 22 | public void shouldGiveBadgeIfScoreOverThreshold() { 23 | Optional badgeType = badgeProcessor 24 | .processForOptionalBadge(160, List.of(), null); 25 | assertThat(badgeType).contains(BadgeType.SILVER); 26 | } 27 | 28 | @Test 29 | public void shouldNotGiveBadgeIfScoreUnderThreshold() { 30 | Optional badgeType = badgeProcessor 31 | .processForOptionalBadge(140, List.of(), null); 32 | assertThat(badgeType).isEmpty(); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter07-master/multiplication/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter07-master/multiplication/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter07-master/multiplication/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/MultiplicationApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MultiplicationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(MultiplicationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/Challenge.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * This class represents a Challenge to solve a Multiplication (a * b). 7 | */ 8 | @Getter 9 | @ToString 10 | @EqualsAndHashCode 11 | @AllArgsConstructor 12 | public class Challenge { 13 | private final int factorA; 14 | private final int factorB; 15 | } 16 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttempt.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | import microservices.book.multiplication.user.User; 5 | 6 | import javax.persistence.*; 7 | 8 | /** 9 | * Identifies the attempt from a {@link User} to solve a challenge. 10 | */ 11 | @Entity 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class ChallengeAttempt { 16 | @Id 17 | @GeneratedValue 18 | private Long id; 19 | @ManyToOne(fetch = FetchType.LAZY) 20 | @JoinColumn(name = "USER_ID") 21 | private User user; 22 | private int factorA; 23 | private int factorB; 24 | private int resultAttempt; 25 | private boolean correct; 26 | } 27 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import javax.validation.Valid; 9 | import java.util.List; 10 | 11 | /** 12 | * This class provides a REST API to POST the attempts from users. 13 | */ 14 | @Slf4j 15 | @RequiredArgsConstructor 16 | @RestController 17 | @RequestMapping("/attempts") 18 | class ChallengeAttemptController { 19 | 20 | private final ChallengeService challengeService; 21 | 22 | @PostMapping 23 | ResponseEntity postResult( 24 | @RequestBody @Valid ChallengeAttemptDTO challengeAttemptDTO) { 25 | return ResponseEntity.ok(challengeService.verifyAttempt(challengeAttemptDTO)); 26 | } 27 | 28 | @GetMapping 29 | ResponseEntity> getStatistics(@RequestParam("alias") String alias) { 30 | return ResponseEntity.ok( 31 | challengeService.getStatsForUser(alias) 32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.Value; 4 | 5 | import javax.validation.constraints.*; 6 | 7 | /** 8 | * Attempt coming from the user 9 | */ 10 | @Value 11 | public class ChallengeAttemptDTO { 12 | 13 | @Min(1) @Max(99) 14 | int factorA, factorB; 15 | @NotBlank 16 | String userAlias; 17 | @Positive(message = "How could you possibly get a negative result here? Try again.") 18 | int guess; 19 | 20 | } -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | import java.util.List; 6 | 7 | public interface ChallengeAttemptRepository extends CrudRepository { 8 | 9 | /** 10 | * @return the last 10 attempts for a given user, identified by their alias. 11 | */ 12 | List findTop10ByUserAliasOrderByIdDesc(String userAlias); 13 | } 14 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | /** 8 | * This class implements a REST API to get random challenges 9 | */ 10 | @Slf4j 11 | @RequiredArgsConstructor 12 | @RestController 13 | @RequestMapping("/challenges") 14 | public class ChallengeController { 15 | 16 | private final ChallengeGeneratorService challengeGeneratorService; 17 | 18 | @GetMapping("/random") 19 | Challenge getRandomChallenge() { 20 | Challenge challenge = challengeGeneratorService.randomChallenge(); 21 | log.info("Generating a random challenge: {}", challenge); 22 | return challenge; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | public interface ChallengeGeneratorService { 4 | 5 | /** 6 | * @return a randomly-generated challenge with factors between 11 and 99 7 | */ 8 | Challenge randomChallenge(); 9 | 10 | } -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorServiceImpl.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.util.Random; 6 | 7 | @Service 8 | public class ChallengeGeneratorServiceImpl implements ChallengeGeneratorService { 9 | 10 | private final static int MINIMUM_FACTOR = 11; 11 | private final static int MAXIMUM_FACTOR = 100; 12 | 13 | private final Random random; 14 | 15 | ChallengeGeneratorServiceImpl() { 16 | this.random = new Random(); 17 | } 18 | 19 | protected ChallengeGeneratorServiceImpl(final Random random) { 20 | this.random = random; 21 | } 22 | 23 | private int next() { 24 | return random.nextInt(MAXIMUM_FACTOR - MINIMUM_FACTOR) + MINIMUM_FACTOR; 25 | } 26 | 27 | @Override 28 | public Challenge randomChallenge() { 29 | return new Challenge(next(), next()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import java.util.List; 4 | 5 | public interface ChallengeService { 6 | 7 | /** 8 | * Verifies if an attempt coming from the presentation layer is correct or not. 9 | * 10 | * @return the resulting ChallengeAttempt object 11 | */ 12 | ChallengeAttempt verifyAttempt(ChallengeAttemptDTO attemptDTO); 13 | 14 | /** 15 | * Gets the statistics for a given user. 16 | * 17 | * @param userAlias the user's alias 18 | * @return a list of the last 10 {@link ChallengeAttempt} 19 | * objects created by the user. 20 | */ 21 | List getStatsForUser(final String userAlias); 22 | } 23 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeSolvedEvent.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.Value; 4 | 5 | @Value 6 | public class ChallengeSolvedEvent { 7 | 8 | long attemptId; 9 | boolean correct; 10 | int factorA; 11 | int factorB; 12 | long userId; 13 | String userAlias; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/configuration/AMQPConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import org.springframework.amqp.core.ExchangeBuilder; 4 | import org.springframework.amqp.core.TopicExchange; 5 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | /** 11 | * Configures RabbitMQ via AMQP abstraction to use events in our application. 12 | */ 13 | @Configuration 14 | public class AMQPConfiguration { 15 | 16 | @Bean 17 | public TopicExchange challengesTopicExchange( 18 | @Value("${amqp.exchange.attempts}") final String exchangeName) { 19 | return ExchangeBuilder.topicExchange(exchangeName).durable(true).build(); 20 | } 21 | 22 | @Bean 23 | public Jackson2JsonMessageConverter producerJackson2MessageConverter() { 24 | return new Jackson2JsonMessageConverter(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/configuration/JsonConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import com.fasterxml.jackson.databind.Module; 4 | import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class JsonConfiguration { 10 | 11 | @Bean 12 | public Module hibernateModule() { 13 | return new Hibernate5Module(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/configuration/WebConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfiguration implements WebMvcConfigurer { 9 | 10 | /** 11 | * Enables Cross-Origin Resource Sharing (CORS) 12 | * More info: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html 13 | */ 14 | @Override 15 | public void addCorsMappings(final CorsRegistry registry) { 16 | registry.addMapping("/**").allowedOrigins("http://localhost:3000"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/user/User.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * Stores information to identify the user. 9 | */ 10 | @Entity 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class User { 15 | 16 | @Id 17 | @GeneratedValue 18 | private Long id; 19 | private String alias; 20 | 21 | public User(final String userAlias) { 22 | this(null, userAlias); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/user/UserController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | @Slf4j 11 | @RequiredArgsConstructor 12 | @RestController 13 | @RequestMapping("/users") 14 | public class UserController { 15 | 16 | private final UserRepository userRepository; 17 | 18 | @GetMapping("/{idList}") 19 | public List getUsersByIdList(@PathVariable final List idList) { 20 | return userRepository.findAllByIdIn(idList); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/java/microservices/book/multiplication/user/UserRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | import org.springframework.data.repository.CrudRepository; 7 | 8 | public interface UserRepository extends CrudRepository { 9 | 10 | Optional findByAlias(final String alias); 11 | 12 | List findAllByIdIn(final List ids); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Includes validation messages in responses 2 | server.error.include-message=always 3 | server.error.include-binding-errors=always 4 | # Gives us access to the H2 database web console 5 | spring.h2.console.enabled=true 6 | # Creates the database in a file 7 | spring.datasource.url=jdbc:h2:file:~/multiplication;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=TRUE; 8 | # Creates or updates the schema if needed 9 | spring.jpa.hibernate.ddl-auto=update 10 | # For educational purposes we will show the SQL in console 11 | #spring.jpa.show-sql=true 12 | 13 | # Gamification service URL 14 | service.gamification.host=http://localhost:8081 15 | 16 | amqp.exchange.attempts=attempts.topic 17 | 18 | # Shows declaration of exchanges, queues, bindings, etc. 19 | logging.level.org.springframework.amqp.rabbit.core.RabbitAdmin = DEBUG 20 | -------------------------------------------------------------------------------- /chapter07-master/multiplication/src/test/java/microservices/book/multiplication/MultiplicationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class MultiplicationApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter07-master/resources/app-chapter7-after10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter07-master/resources/app-chapter7-after10.png -------------------------------------------------------------------------------- /chapter07-master/resources/logical_views-Chapter7-detailed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter07-master/resources/logical_views-Chapter7-detailed.png -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.19 2 | COPY build /usr/share/nginx/html/ 3 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "challenges-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-scripts": "3.4.1" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter08d-master/challenges-frontend/public/favicon.ico -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter08d-master/challenges-frontend/public/logo192.png -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter08d-master/challenges-frontend/public/logo512.png -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .display-column { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .challenge { 8 | font-size: 4em; 9 | } 10 | 11 | th { 12 | padding-right: 0.5em; 13 | border-bottom: solid 1px; 14 | } 15 | 16 | .badge { 17 | font-size: x-small; 18 | border: 2px solid dodgerblue; 19 | border-radius: 4px; 20 | padding: 0.2em; 21 | margin: 0.1em; 22 | } 23 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.css'; 3 | import ChallengeComponent from './components/ChallengeComponent'; 4 | 5 | function App() { 6 | return ; 7 | } 8 | 9 | export default App; 10 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/src/components/LastAttemptsComponent.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | class LastAttemptsComponent extends React.Component { 4 | 5 | render() { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {this.props.lastAttempts.map(a => 17 | 19 | 20 | 21 | 23 | 24 | )} 25 | 26 |
ChallengeYour guessCorrect
{a.factorA} x {a.factorB}{a.resultAttempt}{a.correct ? "Correct" : 22 | ("Incorrect (" + a.factorA * a.factorB + ")")}
27 | ); 28 | } 29 | } 30 | 31 | export default LastAttemptsComponent; 32 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Segoe UI', Roboto, Arial, sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://bit.ly/CRA-PWA 17 | serviceWorker.unregister(); 18 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/src/services/GameApiClient.js: -------------------------------------------------------------------------------- 1 | class GameApiClient { 2 | static SERVER_URL = 'http://localhost:8000'; 3 | static GET_LEADERBOARD = '/leaders'; 4 | 5 | static leaderBoard(): Promise { 6 | return fetch(GameApiClient.SERVER_URL + 7 | GameApiClient.GET_LEADERBOARD); 8 | } 9 | 10 | } 11 | 12 | export default GameApiClient; 13 | -------------------------------------------------------------------------------- /chapter08d-master/challenges-frontend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /chapter08d-master/docker/consul/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM consul:1.7.2 2 | COPY ./consul-kv-docker.json /usr/src/consul/ 3 | WORKDIR /usr/src/consul 4 | ENV CONSUL_HTTP_ADDR=consul:8500 5 | ENTRYPOINT until consul kv import @consul-kv-docker.json; do echo "Waiting for Consul"; sleep 2; done 6 | -------------------------------------------------------------------------------- /chapter08d-master/docker/consul/consul-kv-docker.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "config/", 4 | "flags": 0, 5 | "value": "" 6 | }, 7 | { 8 | "key": "config/defaults,docker/", 9 | "flags": 0, 10 | "value": "" 11 | }, 12 | { 13 | "key": "config/defaults,docker/application.yml", 14 | "flags": 0, 15 | "value": "c3ByaW5nOgogIHJhYmJpdG1xOgogICAgaG9zdDogcmFiYml0bXEKICBjbG91ZDoKICAgIGNvbnN1bDoKICAgICAgZGlzY292ZXJ5OgogICAgICAgIGluc3RhbmNlLWlkOiAke3NwcmluZy5hcHBsaWNhdGlvbi5uYW1lfS0ke3JhbmRvbS5pbnQoMTAwMCl9" 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /chapter08d-master/docker/docker.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter08d-master/gamification/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter08d-master/gamification/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/game-layers.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | skinparam dpi 300 3 | scale 3 4 | skinparam class { 5 | BackgroundColor Ivory 6 | ArrowColor DarkGrey 7 | BorderColor Silver 8 | } 9 | 10 | interface LeaderBoardService 11 | 12 | LeaderBoardController "1" o-- "1" LeaderBoardService : uses > 13 | LeaderBoardServiceImpl .up.|> LeaderBoardService : implements 14 | LeaderBoardServiceImpl "1" o-down- "1" BadgeRepository : uses > 15 | LeaderBoardServiceImpl "1" o-down- "1" ScoreRepository : uses > 16 | @enduml 17 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/leaderboard-layers.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | skinparam dpi 300 3 | scale 3 4 | skinparam class { 5 | BackgroundColor Ivory 6 | ArrowColor DarkGrey 7 | BorderColor Silver 8 | } 9 | 10 | interface GameService 11 | interface BadgeProcessor 12 | 13 | GameController "1" o-- "1" GameService : uses > 14 | GameServiceImpl .|> GameService : implements 15 | GameServiceImpl "1" o-up- "*" BadgeProcessor : uses many 16 | GameServiceImpl "1" o-down- "1" BadgeRepository : uses > 17 | GameServiceImpl "1" o-down- "1" ScoreRepository : uses > 18 | @enduml 19 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/GamificationApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class GamificationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(GamificationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/challenge/ChallengeSolvedEvent.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.challenge; 2 | 3 | import lombok.Value; 4 | 5 | @Value 6 | public class ChallengeSolvedEvent { 7 | 8 | long attemptId; 9 | boolean correct; 10 | int factorA; 11 | int factorB; 12 | long userId; 13 | String userAlias; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/configuration/JsonConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.configuration; 2 | 3 | import com.fasterxml.jackson.databind.Module; 4 | import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class JsonConfiguration { 10 | 11 | @Bean 12 | public Module hibernateModule() { 13 | return new Hibernate5Module(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/BadgeRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import microservices.book.gamification.game.domain.BadgeCard; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import org.springframework.data.repository.CrudRepository; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Handles data operations with BadgeCards 11 | */ 12 | public interface BadgeRepository extends CrudRepository { 13 | 14 | /** 15 | * Retrieves all BadgeCards for a given user. 16 | * 17 | * @param userId the id of the user to look for BadgeCards 18 | * @return the list of BadgeCards, sorted by most recent. 19 | */ 20 | List findByUserIdOrderByBadgeTimestampDesc(final Long userId); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/GameService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import java.util.List; 4 | 5 | import lombok.Value; 6 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 7 | import microservices.book.gamification.game.domain.BadgeType; 8 | 9 | /** 10 | * This service includes the main logic for gamifying the system. 11 | */ 12 | public interface GameService { 13 | 14 | /** 15 | * Process a new attempt from a given user. 16 | * 17 | * @param challenge the challenge data with user details, factors, etc. 18 | * @return a {@link GameResult} object containing the new score and badge cards obtained 19 | */ 20 | GameResult newAttemptForUser(ChallengeSolvedEvent challenge); 21 | 22 | @Value 23 | class GameResult { 24 | int score; 25 | List badges; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/LeaderBoardController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import microservices.book.gamification.game.domain.LeaderBoardRow; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * This class implements a REST API for the Gamification LeaderBoard service. 11 | */ 12 | @RestController 13 | @RequestMapping("/leaders") 14 | @RequiredArgsConstructor 15 | class LeaderBoardController { 16 | 17 | private final LeaderBoardService leaderBoardService; 18 | 19 | @GetMapping 20 | public List getLeaderBoard() { 21 | return leaderBoardService.getCurrentLeaderBoard(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/LeaderBoardService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game; 2 | 3 | import java.util.List; 4 | 5 | import microservices.book.gamification.game.domain.LeaderBoardRow; 6 | 7 | public interface LeaderBoardService { 8 | 9 | /** 10 | * @return the current leader board ranked from high to low score 11 | */ 12 | List getCurrentLeaderBoard(); 13 | } 14 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/BadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 7 | import microservices.book.gamification.game.domain.BadgeType; 8 | import microservices.book.gamification.game.domain.ScoreCard; 9 | 10 | public interface BadgeProcessor { 11 | 12 | /** 13 | * Processes some or all of the passed parameters and decides if the user 14 | * is entitled to a badge. 15 | * 16 | * @return a BadgeType if the user is entitled to this badge, otherwise empty 17 | */ 18 | Optional processForOptionalBadge(int currentScore, 19 | List scoreCardList, 20 | ChallengeSolvedEvent solved); 21 | 22 | /** 23 | * @return the BadgeType object that this processor is handling. You can use 24 | * it to filter processors according to your needs. 25 | */ 26 | BadgeType badgeType(); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/BronzeBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class BronzeBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedEvent solved) { 18 | return currentScore > 50 ? 19 | Optional.of(BadgeType.BRONZE) : 20 | Optional.empty(); 21 | } 22 | 23 | @Override 24 | public BadgeType badgeType() { 25 | return BadgeType.BRONZE; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/FirstWonBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class FirstWonBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedEvent solved) { 18 | return scoreCardList.size() == 1 ? 19 | Optional.of(BadgeType.FIRST_WON) : Optional.empty(); 20 | } 21 | 22 | @Override 23 | public BadgeType badgeType() { 24 | return BadgeType.FIRST_WON; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/GoldBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class GoldBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedEvent solved) { 18 | return currentScore > 400 ? 19 | Optional.of(BadgeType.GOLD) : 20 | Optional.empty(); 21 | } 22 | 23 | @Override 24 | public BadgeType badgeType() { 25 | return BadgeType.GOLD; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/badgeprocessors/SilverBadgeProcessor.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.challenge.ChallengeSolvedEvent; 4 | import microservices.book.gamification.game.domain.BadgeType; 5 | import microservices.book.gamification.game.domain.ScoreCard; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | import java.util.Optional; 10 | 11 | @Component 12 | class SilverBadgeProcessor implements BadgeProcessor { 13 | 14 | @Override 15 | public Optional processForOptionalBadge(int currentScore, 16 | List scoreCardList, 17 | ChallengeSolvedEvent solved) { 18 | return currentScore > 150 ? 19 | Optional.of(BadgeType.SILVER) : 20 | Optional.empty(); 21 | } 22 | 23 | @Override 24 | public BadgeType badgeType() { 25 | return BadgeType.SILVER; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/domain/BadgeCard.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * This class links a Badge to a User. Contains also a timestamp with the moment in which the user got it. 9 | */ 10 | @Entity 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class BadgeCard { 15 | 16 | @Id 17 | @GeneratedValue 18 | private Long badgeId; 19 | 20 | private Long userId; 21 | @EqualsAndHashCode.Exclude 22 | private long badgeTimestamp; 23 | private BadgeType badgeType; 24 | 25 | public BadgeCard(final Long userId, final BadgeType badgeType) { 26 | this(null, userId, System.currentTimeMillis(), badgeType); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/domain/BadgeType.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | /** 7 | * Enumeration with the different types of Badges that a user can win. 8 | */ 9 | @RequiredArgsConstructor 10 | @Getter 11 | public enum BadgeType { 12 | 13 | // Badges depending on score 14 | BRONZE("Bronze"), 15 | SILVER("Silver"), 16 | GOLD("Gold"), 17 | 18 | // Other badges won for different conditions 19 | FIRST_WON("First time"), 20 | LUCKY_NUMBER("Lucky number"); 21 | 22 | private final String description; 23 | } 24 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/domain/LeaderBoardRow.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.*; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * Represents a line in our Leaderboard. 9 | */ 10 | @Value 11 | @AllArgsConstructor 12 | public class LeaderBoardRow { 13 | 14 | Long userId; 15 | Long totalScore; 16 | @With 17 | List badges; 18 | 19 | public LeaderBoardRow(final Long userId, final Long totalScore) { 20 | this.userId = userId; 21 | this.totalScore = totalScore; 22 | this.badges = List.of(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/java/microservices/book/gamification/game/domain/ScoreCard.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.domain; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * This class represents the Score linked to an attempt in the game, 9 | * with an associated user and the timestamp in which the score 10 | * is registered. 11 | */ 12 | @Entity 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class ScoreCard { 17 | 18 | // The default score assigned to this card, if not specified. 19 | public static final int DEFAULT_SCORE = 10; 20 | 21 | @Id 22 | @GeneratedValue 23 | private Long cardId; 24 | private Long userId; 25 | private Long attemptId; 26 | @EqualsAndHashCode.Exclude 27 | private long scoreTimestamp; 28 | private int score; 29 | 30 | public ScoreCard(final Long userId, final Long attemptId) { 31 | this(null, userId, attemptId, System.currentTimeMillis(), DEFAULT_SCORE); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=gamification 2 | server.port=8081 3 | # Gives us access to the H2 database web console 4 | spring.h2.console.enabled=true 5 | # Creates the database in a file 6 | spring.datasource.url=jdbc:h2:file:~/gamification;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=TRUE; 7 | # Creates or updates the schema if needed 8 | spring.jpa.hibernate.ddl-auto=update 9 | # For educational purposes we will show the SQL in console 10 | #spring.jpa.show-sql=true 11 | 12 | amqp.exchange.attempts=attempts.topic 13 | amqp.queue.gamification=gamification.queue 14 | 15 | # Shows declaration of exchanges, queues, bindings, etc. 16 | logging.level.org.springframework.amqp.rabbit.core.RabbitAdmin = DEBUG 17 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.consul.config.prefix=config 2 | spring.cloud.consul.config.format=yaml 3 | spring.cloud.consul.config.default-context=defaults 4 | spring.cloud.consul.config.data-key=application.yml 5 | logging.level.org.springframework.amqp.rabbit.connection.CachingConnectionFactory = WARN 6 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | %d{HH:mm:ss.SSS} [%t] %logger{36} - %msg 12 | 13 | 14 | gamification 15 | ${rabbitMQHost:-localhost} 16 | %property{applicationId}.%p 17 | logs.topic 18 | true 19 | UTF-8 20 | true 21 | true 22 | PERSISTENT 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/test/java/microservices/book/gamification/GamificationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.TestPropertySource; 6 | 7 | @SpringBootTest 8 | @TestPropertySource("classpath:/test.properties") 9 | class GamificationApplicationTests { 10 | 11 | @Test 12 | void contextLoads() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/test/java/microservices/book/gamification/game/badgeprocessors/BronzeBadgeProcessorTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.game.domain.BadgeType; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class BronzeBadgeProcessorTest { 13 | 14 | private BronzeBadgeProcessor badgeProcessor; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | badgeProcessor = new BronzeBadgeProcessor(); 19 | } 20 | 21 | @Test 22 | public void shouldGiveBadgeIfScoreOverThreshold() { 23 | Optional badgeType = badgeProcessor 24 | .processForOptionalBadge(60, List.of(), null); 25 | assertThat(badgeType).contains(BadgeType.BRONZE); 26 | } 27 | 28 | @Test 29 | public void shouldNotGiveBadgeIfScoreUnderThreshold() { 30 | Optional badgeType = badgeProcessor 31 | .processForOptionalBadge(40, List.of(), null); 32 | assertThat(badgeType).isEmpty(); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/test/java/microservices/book/gamification/game/badgeprocessors/GoldBadgeProcessorTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.game.domain.BadgeType; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class GoldBadgeProcessorTest { 13 | 14 | private GoldBadgeProcessor badgeProcessor; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | badgeProcessor = new GoldBadgeProcessor(); 19 | } 20 | 21 | @Test 22 | public void shouldGiveBadgeIfScoreOverThreshold() { 23 | Optional badgeType = badgeProcessor 24 | .processForOptionalBadge(450, List.of(), null); 25 | assertThat(badgeType).contains(BadgeType.GOLD); 26 | } 27 | 28 | @Test 29 | public void shouldNotGiveBadgeIfScoreUnderThreshold() { 30 | Optional badgeType = badgeProcessor 31 | .processForOptionalBadge(350, List.of(), null); 32 | assertThat(badgeType).isEmpty(); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/test/java/microservices/book/gamification/game/badgeprocessors/SilverBadgeProcessorTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gamification.game.badgeprocessors; 2 | 3 | import microservices.book.gamification.game.domain.BadgeType; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class SilverBadgeProcessorTest { 13 | 14 | private SilverBadgeProcessor badgeProcessor; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | badgeProcessor = new SilverBadgeProcessor(); 19 | } 20 | 21 | @Test 22 | public void shouldGiveBadgeIfScoreOverThreshold() { 23 | Optional badgeType = badgeProcessor 24 | .processForOptionalBadge(160, List.of(), null); 25 | assertThat(badgeType).contains(BadgeType.SILVER); 26 | } 27 | 28 | @Test 29 | public void shouldNotGiveBadgeIfScoreUnderThreshold() { 30 | Optional badgeType = badgeProcessor 31 | .processForOptionalBadge(140, List.of(), null); 32 | assertThat(badgeType).isEmpty(); 33 | } 34 | } -------------------------------------------------------------------------------- /chapter08d-master/gamification/src/test/resources/test.properties: -------------------------------------------------------------------------------- 1 | # Disable Consul Config for tests. Search for the `TestPropertySource` 2 | # annotation to find out which tests require this. 3 | spring.cloud.consul.config.enabled=false 4 | -------------------------------------------------------------------------------- /chapter08d-master/gateway/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter08d-master/gateway/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter08d-master/gateway/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter08d-master/gateway/src/main/java/microservices/book/gateway/GatewayApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gateway; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class GatewayApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(GatewayApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter08d-master/gateway/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | consul: 4 | config: 5 | data-key: application.yml 6 | prefix: config 7 | format: yaml 8 | default-context: defaults 9 | logging: 10 | level: 11 | org.springframework.amqp.rabbit.connection.CachingConnectionFactory: WARN 12 | -------------------------------------------------------------------------------- /chapter08d-master/gateway/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | %d{HH:mm:ss.SSS} [%t] %logger{36} - %msg 12 | 13 | 14 | gateway 15 | ${rabbitMQHost:-localhost} 16 | %property{applicationId}.%p 17 | logs.topic 18 | true 19 | UTF-8 20 | true 21 | true 22 | PERSISTENT 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /chapter08d-master/gateway/src/test/java/microservices/book/gateway/GatewayApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.gateway; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.TestPropertySource; 6 | 7 | @SpringBootTest 8 | @TestPropertySource("classpath:/test.properties") 9 | class GatewayApplicationTests { 10 | 11 | @Test 12 | void contextLoads() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chapter08d-master/gateway/src/test/resources/test.properties: -------------------------------------------------------------------------------- 1 | # Disable Consul Config for tests. Search for the `TestPropertySource` 2 | # annotation to find out which tests require this. 3 | spring.cloud.consul.config.enabled=false 4 | -------------------------------------------------------------------------------- /chapter08d-master/logs/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter08d-master/logs/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter08d-master/logs/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter08d-master/logs/src/main/java/microservices/book/logs/AMQPConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.logs; 2 | 3 | import org.springframework.amqp.core.*; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class AMQPConfiguration { 9 | 10 | @Bean 11 | public TopicExchange logsExchange() { 12 | return ExchangeBuilder.topicExchange("logs.topic") 13 | .durable(true) 14 | .build(); 15 | } 16 | 17 | @Bean 18 | public Queue logsQueue() { 19 | return QueueBuilder.durable("logs.queue").build(); 20 | } 21 | 22 | @Bean 23 | public Binding logsBinding(final Queue logsQueue, 24 | final TopicExchange logsExchange) { 25 | return BindingBuilder.bind(logsQueue) 26 | .to(logsExchange).with("#"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter08d-master/logs/src/main/java/microservices/book/logs/LogsApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.logs; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class LogsApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(LogsApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter08d-master/logs/src/main/java/microservices/book/logs/LogsConsumer.java: -------------------------------------------------------------------------------- 1 | package microservices.book.logs; 2 | 3 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 4 | import org.springframework.messaging.handler.annotation.Header; 5 | import org.springframework.stereotype.Service; 6 | 7 | import org.slf4j.Marker; 8 | import org.slf4j.MarkerFactory; 9 | 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | @Slf4j 13 | @Service 14 | public class LogsConsumer { 15 | 16 | @RabbitListener(queues = "logs.queue") 17 | public void log(final String msg, 18 | @Header("level") String level, 19 | @Header("amqp_appId") String appId) { 20 | Marker marker = MarkerFactory.getMarker(appId); 21 | switch (level) { 22 | case "INFO" -> log.info(marker, msg); 23 | case "ERROR" -> log.error(marker, msg); 24 | case "WARN" -> log.warn(marker, msg); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter08d-master/logs/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=logs 2 | server.port=8580 3 | -------------------------------------------------------------------------------- /chapter08d-master/logs/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.consul.config.prefix=config 2 | spring.cloud.consul.config.format=yaml 3 | spring.cloud.consul.config.default-context=defaults 4 | spring.cloud.consul.config.data-key=application.yml 5 | -------------------------------------------------------------------------------- /chapter08d-master/logs/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | [%-15marker] [%X{X-B3-TraceId:-},%X{X-B3-SpanId:-}] %highlight(%-5level) %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /chapter08d-master/logs/src/test/java/microservices/book/logs/LogsApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.logs; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.TestPropertySource; 6 | 7 | @SpringBootTest 8 | @TestPropertySource("classpath:/test.properties") 9 | class LogsApplicationTests { 10 | 11 | @Test 12 | void contextLoads() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chapter08d-master/logs/src/test/resources/test.properties: -------------------------------------------------------------------------------- 1 | # Disable Consul Config for tests. Search for the `TestPropertySource` 2 | # annotation to find out which tests require this. 3 | spring.cloud.consul.config.enabled=false 4 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter08d-master/multiplication/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter08d-master/multiplication/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:14 2 | COPY ./target/multiplication-0.0.1-SNAPSHOT.jar /usr/src/multiplication/ 3 | WORKDIR /usr/src/multiplication 4 | EXPOSE 8080 5 | CMD ["java", "-jar", "multiplication-0.0.1-SNAPSHOT.jar"] 6 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/MultiplicationApplication.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MultiplicationApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(MultiplicationApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/challenge/Challenge.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | 5 | /** 6 | * This class represents a Challenge to solve a Multiplication (a * b). 7 | */ 8 | @Getter 9 | @ToString 10 | @EqualsAndHashCode 11 | @AllArgsConstructor 12 | public class Challenge { 13 | private final int factorA; 14 | private final int factorB; 15 | } 16 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttempt.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.*; 4 | import microservices.book.multiplication.user.User; 5 | 6 | import javax.persistence.*; 7 | 8 | /** 9 | * Identifies the attempt from a {@link User} to solve a challenge. 10 | */ 11 | @Entity 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class ChallengeAttempt { 16 | @Id 17 | @GeneratedValue 18 | private Long id; 19 | @ManyToOne(fetch = FetchType.LAZY) 20 | @JoinColumn(name = "USER_ID") 21 | private User user; 22 | private int factorA; 23 | private int factorB; 24 | private int resultAttempt; 25 | private boolean correct; 26 | } 27 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.Value; 4 | 5 | import javax.validation.constraints.*; 6 | 7 | /** 8 | * Attempt coming from the user 9 | */ 10 | @Value 11 | public class ChallengeAttemptDTO { 12 | 13 | @Min(1) @Max(99) 14 | int factorA, factorB; 15 | @NotBlank 16 | String userAlias; 17 | @Positive(message = "How could you possibly get a negative result here? Try again.") 18 | int guess; 19 | 20 | } -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeAttemptRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | import java.util.List; 6 | 7 | public interface ChallengeAttemptRepository extends CrudRepository { 8 | 9 | /** 10 | * @return the last 10 attempts for a given user, identified by their alias. 11 | */ 12 | List findTop10ByUserAliasOrderByIdDesc(String userAlias); 13 | } 14 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | /** 8 | * This class implements a REST API to get random challenges 9 | */ 10 | @Slf4j 11 | @RequiredArgsConstructor 12 | @RestController 13 | @RequestMapping("/challenges") 14 | public class ChallengeController { 15 | 16 | private final ChallengeGeneratorService challengeGeneratorService; 17 | 18 | @GetMapping("/random") 19 | Challenge getRandomChallenge() { 20 | Challenge challenge = challengeGeneratorService.randomChallenge(); 21 | log.info("Generating a random challenge: {}", challenge); 22 | return challenge; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | public interface ChallengeGeneratorService { 4 | 5 | /** 6 | * @return a randomly-generated challenge with factors between 11 and 99 7 | */ 8 | Challenge randomChallenge(); 9 | 10 | } -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeGeneratorServiceImpl.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.util.Random; 6 | 7 | @Service 8 | public class ChallengeGeneratorServiceImpl implements ChallengeGeneratorService { 9 | 10 | private final static int MINIMUM_FACTOR = 11; 11 | private final static int MAXIMUM_FACTOR = 100; 12 | 13 | private final Random random; 14 | 15 | ChallengeGeneratorServiceImpl() { 16 | this.random = new Random(); 17 | } 18 | 19 | protected ChallengeGeneratorServiceImpl(final Random random) { 20 | this.random = random; 21 | } 22 | 23 | private int next() { 24 | return random.nextInt(MAXIMUM_FACTOR - MINIMUM_FACTOR) + MINIMUM_FACTOR; 25 | } 26 | 27 | @Override 28 | public Challenge randomChallenge() { 29 | return new Challenge(next(), next()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeService.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import java.util.List; 4 | 5 | public interface ChallengeService { 6 | 7 | /** 8 | * Verifies if an attempt coming from the presentation layer is correct or not. 9 | * 10 | * @return the resulting ChallengeAttempt object 11 | */ 12 | ChallengeAttempt verifyAttempt(ChallengeAttemptDTO attemptDTO); 13 | 14 | /** 15 | * Gets the statistics for a given user. 16 | * 17 | * @param userAlias the user's alias 18 | * @return a list of the last 10 {@link ChallengeAttempt} 19 | * objects created by the user. 20 | */ 21 | List getStatsForUser(final String userAlias); 22 | } 23 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/challenge/ChallengeSolvedEvent.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.challenge; 2 | 3 | import lombok.Value; 4 | 5 | @Value 6 | public class ChallengeSolvedEvent { 7 | 8 | long attemptId; 9 | boolean correct; 10 | int factorA; 11 | int factorB; 12 | long userId; 13 | String userAlias; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/configuration/AMQPConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import org.springframework.amqp.core.ExchangeBuilder; 4 | import org.springframework.amqp.core.TopicExchange; 5 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | /** 11 | * Configures RabbitMQ via AMQP abstraction to use events in our application. 12 | */ 13 | @Configuration 14 | public class AMQPConfiguration { 15 | 16 | @Bean 17 | public TopicExchange challengesTopicExchange( 18 | @Value("${amqp.exchange.attempts}") final String exchangeName) { 19 | return ExchangeBuilder.topicExchange(exchangeName).durable(true).build(); 20 | } 21 | 22 | @Bean 23 | public Jackson2JsonMessageConverter producerJackson2MessageConverter() { 24 | return new Jackson2JsonMessageConverter(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/configuration/JsonConfiguration.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.configuration; 2 | 3 | import com.fasterxml.jackson.databind.Module; 4 | import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class JsonConfiguration { 10 | 11 | @Bean 12 | public Module hibernateModule() { 13 | return new Hibernate5Module(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/user/User.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | 7 | /** 8 | * Stores information to identify the user. 9 | */ 10 | @Entity 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class User { 15 | 16 | @Id 17 | @GeneratedValue 18 | private Long id; 19 | private String alias; 20 | 21 | public User(final String userAlias) { 22 | this(null, userAlias); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/user/UserController.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | @Slf4j 11 | @RequiredArgsConstructor 12 | @RestController 13 | @RequestMapping("/users") 14 | public class UserController { 15 | 16 | private final UserRepository userRepository; 17 | 18 | @GetMapping("/{idList}") 19 | public List getUsersByIdList(@PathVariable final List idList) { 20 | return userRepository.findAllByIdIn(idList); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/java/microservices/book/multiplication/user/UserRepository.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication.user; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | import org.springframework.data.repository.CrudRepository; 7 | 8 | public interface UserRepository extends CrudRepository { 9 | 10 | Optional findByAlias(final String alias); 11 | 12 | List findAllByIdIn(final List ids); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=multiplication 2 | # Gives us access to the H2 database web console 3 | spring.h2.console.enabled=true 4 | # Creates the database in a file 5 | spring.datasource.url=jdbc:h2:file:~/multiplication;DB_CLOSE_ON_EXIT=FALSE;AUTO_SERVER=TRUE; 6 | # Creates or updates the schema if needed 7 | spring.jpa.hibernate.ddl-auto=update 8 | # For educational purposes we will show the SQL in console 9 | #spring.jpa.show-sql=true 10 | 11 | # Gamification service URL 12 | service.gamification.host=http://localhost:8081 13 | 14 | amqp.exchange.attempts=attempts.topic 15 | 16 | # Shows declaration of exchanges, queues, bindings, etc. 17 | logging.level.org.springframework.amqp.rabbit.core.RabbitAdmin = DEBUG 18 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.consul.config.prefix=config 2 | spring.cloud.consul.config.format=yaml 3 | spring.cloud.consul.config.default-context=defaults 4 | spring.cloud.consul.config.data-key=application.yml 5 | logging.level.org.springframework.amqp.rabbit.connection.CachingConnectionFactory = WARN 6 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | %d{HH:mm:ss.SSS} [%t] %logger{36} - %msg 12 | 13 | 14 | multiplication 15 | ${rabbitMQHost:-localhost} 16 | %property{applicationId}.%p 17 | logs.topic 18 | true 19 | UTF-8 20 | true 21 | true 22 | PERSISTENT 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/test/java/microservices/book/multiplication/MultiplicationApplicationTests.java: -------------------------------------------------------------------------------- 1 | package microservices.book.multiplication; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.TestPropertySource; 6 | 7 | @SpringBootTest 8 | @TestPropertySource("classpath:/test.properties") 9 | class MultiplicationApplicationTests { 10 | 11 | @Test 12 | void contextLoads() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /chapter08d-master/multiplication/src/test/resources/test.properties: -------------------------------------------------------------------------------- 1 | # Disable Consul Config for tests. Search for the `TestPropertySource` 2 | # annotation to find out which tests require this. 3 | spring.cloud.consul.config.enabled=false 4 | -------------------------------------------------------------------------------- /chapter08d-master/resources/microservice_patterns-Config-Server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter08d-master/resources/microservice_patterns-Config-Server.png -------------------------------------------------------------------------------- /chapter08d-master/resources/microservice_patterns-View-Containers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/chapter08d-master/resources/microservice_patterns-View-Containers.png -------------------------------------------------------------------------------- /cucumber-tests-master/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/cucumber-tests-master/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /cucumber-tests-master/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /cucumber-tests-master/docs/components_plantuml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/cucumber-tests-master/docs/components_plantuml.png -------------------------------------------------------------------------------- /cucumber-tests-master/docs/components_plantuml.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | component api as "Api Client" 3 | collections dtos as "DTOs" 4 | collections steps as "Step Definition Files" 5 | collections gherkin as "Gherkin files" 6 | component entry as "RunCucumberTest" 7 | component Challenge 8 | component User 9 | 10 | api "1" *.right.> "*" dtos : uses 11 | entry ..> gherkin : executes 12 | gherkin .> steps : map to 13 | steps "1" *--> "*" Challenge : creates 14 | steps "1" *--> "*" User : creates 15 | Challenge "1" *--> "1" api : uses 16 | User "1" *--> "1" api : uses 17 | @enduml 18 | -------------------------------------------------------------------------------- /cucumber-tests-master/docs/cucumber_project_uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/learn-microservices-spring-boot-2e/31ef146e62d10c89e53b124550cef578b07d8a28/cucumber-tests-master/docs/cucumber_project_uml.png -------------------------------------------------------------------------------- /cucumber-tests-master/src/test/java/microservices/book/RunCucumberTest.java: -------------------------------------------------------------------------------- 1 | package microservices.book; 2 | 3 | import io.cucumber.junit.Cucumber; 4 | import io.cucumber.junit.CucumberOptions; 5 | import org.junit.runner.RunWith; 6 | 7 | @RunWith(Cucumber.class) 8 | @CucumberOptions(plugin = {"pretty"}) 9 | public class RunCucumberTest { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /cucumber-tests-master/src/test/java/microservices/book/cucumber/api/dtos/challenge/AttemptResponseDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.cucumber.api.dtos.challenge; 2 | 3 | import microservices.book.cucumber.api.dtos.users.UserDTO; 4 | 5 | public class AttemptResponseDTO { 6 | 7 | private int resultAttempt; 8 | private boolean correct; 9 | private UserDTO user; 10 | 11 | public AttemptResponseDTO() { 12 | } 13 | 14 | public int getResultAttempt() { 15 | return resultAttempt; 16 | } 17 | 18 | public boolean isCorrect() { 19 | return correct; 20 | } 21 | 22 | public UserDTO getUser() { 23 | return user; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /cucumber-tests-master/src/test/java/microservices/book/cucumber/api/dtos/challenge/ChallengeDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.cucumber.api.dtos.challenge; 2 | 3 | public class ChallengeDTO { 4 | private final int factorA; 5 | private final int factorB; 6 | 7 | public ChallengeDTO() { 8 | this.factorA = -1; 9 | this.factorB = -1; 10 | } 11 | 12 | public int getFactorA() { 13 | return factorA; 14 | } 15 | 16 | public int getFactorB() { 17 | return factorB; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "ChallengeDTO{" + 23 | "factorA=" + factorA + 24 | ", factorB=" + factorB + 25 | '}'; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cucumber-tests-master/src/test/java/microservices/book/cucumber/api/dtos/leaderboard/LeaderboardRowDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.cucumber.api.dtos.leaderboard; 2 | 3 | import java.util.List; 4 | 5 | public class LeaderboardRowDTO { 6 | 7 | private long userId; 8 | private long totalScore; 9 | private List badges; 10 | 11 | public LeaderboardRowDTO() { 12 | } 13 | 14 | public long getUserId() { 15 | return userId; 16 | } 17 | 18 | public long getTotalScore() { 19 | return totalScore; 20 | } 21 | 22 | public List getBadges() { 23 | return badges; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cucumber-tests-master/src/test/java/microservices/book/cucumber/api/dtos/users/UserDTO.java: -------------------------------------------------------------------------------- 1 | package microservices.book.cucumber.api.dtos.users; 2 | 3 | public class UserDTO { 4 | private long id; 5 | private String alias; 6 | 7 | public UserDTO() { 8 | } 9 | 10 | public long getId() { 11 | return id; 12 | } 13 | 14 | public String getAlias() { 15 | return alias; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /cucumber-tests-master/src/test/resources/cucumber.properties: -------------------------------------------------------------------------------- 1 | cucumber.publish.enabled=true 2 | -------------------------------------------------------------------------------- /cucumber-tests-master/src/test/resources/microservices/book/leaderboard.feature: -------------------------------------------------------------------------------- 1 | Feature: The Leaderboard shows a ranking with all the users who solved 2 | challenges correctly. It displays them ordered by the highest score first. 3 | 4 | Scenario: Users get points and badges when solving challenges, and they 5 | are positioned accordingly in the Leaderboard. 6 | Given the following solved challenges 7 | | user | solved_challenges | 8 | | Karen | 5 | 9 | | Laura | 7 | 10 | Then Karen has 50 points 11 | * Karen has the "First time" badge 12 | And Laura has 70 points 13 | * Laura has the "First time" badge 14 | * Laura has the "Bronze" badge 15 | And Laura is above Karen in the ranking 16 | 17 | 18 | -------------------------------------------------------------------------------- /cucumber-tests-master/src/test/resources/microservices/book/solving_challenges.feature: -------------------------------------------------------------------------------- 1 | Feature: Solve multiplication challenges 2 | We present users with challenges they should solve using mental calculation 3 | only. When they're right, we give them score and, in some cases, new badges. 4 | All attempts from all users are stored, so they can see their historical data. 5 | 6 | Scenario: Users get new attempts. 7 | Given a new user Mary 8 | When she requests a new challenge 9 | Then she gets a mid-complexity multiplication to solve 10 | 11 | Scenario: Users solve challenges, they get feedback and their stats. 12 | Given a new user John 13 | And he requests a new challenge 14 | When he sends the correct challenge solution 15 | Then his stats include 1 correct attempt 16 | 17 | Scenario: Users get feedback also about incorrect attempts. 18 | Given a new user Horatio 19 | When he requests a new challenge 20 | * he sends the incorrect challenge solution 21 | * he sends the correct challenge solution 22 | Then his stats include 1 correct attempt 23 | * his stats include 1 incorrect attempts 24 | -------------------------------------------------------------------------------- /errata.md: -------------------------------------------------------------------------------- 1 | # Errata for *Learn Microservices with Spring Boot, 2nd Edition* 2 | 3 | On **page xx** [Summary of error]: 4 | 5 | Details of error here. Highlight key pieces in **bold**. 6 | 7 | *** 8 | 9 | On **page xx** [Summary of error]: 10 | 11 | Details of error here. Highlight key pieces in **bold**. 12 | 13 | *** --------------------------------------------------------------------------------