├── Chapter03 └── investor-services │ ├── pom.xml │ ├── scripts │ ├── book-examples.postman_collection.json │ ├── buildMyExamples.bat │ └── runMyExamples.bat │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── books │ │ │ └── chapters │ │ │ └── restfulapi │ │ │ └── patterns │ │ │ └── chap3 │ │ │ └── springboot │ │ │ ├── InvestorServicesApplication.java │ │ │ ├── controller │ │ │ └── InvestorController.java │ │ │ ├── models │ │ │ ├── IndividualInvestorPortfolio.java │ │ │ ├── Investor.java │ │ │ ├── Stock.java │ │ │ └── errorsandexceptions │ │ │ │ └── InvestorNotFoundException.java │ │ │ └── service │ │ │ └── InvestorService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── books │ └── chapters │ └── restfulapi │ └── patterns │ └── chap3 │ └── springboot │ ├── InvestorServicesApplicationTests.java │ ├── controller │ └── InvestorControllerTest.java │ └── service │ └── InvestorServiceTest.java ├── Chapter04 ├── circuit-breaker-examples │ ├── circuit-breaker-service-consumer │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── books │ │ │ │ │ └── chapters │ │ │ │ │ └── restfulapi │ │ │ │ │ └── patterns │ │ │ │ │ └── chap4 │ │ │ │ │ └── springboot │ │ │ │ │ ├── InvestorServicesApplication.java │ │ │ │ │ ├── controller │ │ │ │ │ └── InvestorController.java │ │ │ │ │ ├── models │ │ │ │ │ ├── IndividualInvestorPortfolio.java │ │ │ │ │ ├── Investor.java │ │ │ │ │ ├── Stock.java │ │ │ │ │ └── errorsandexceptions │ │ │ │ │ │ └── InvestorNotFoundException.java │ │ │ │ │ └── service │ │ │ │ │ └── InvestorService.java │ │ │ └── resources │ │ │ │ └── application.properties │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── books │ │ │ └── chapters │ │ │ └── restfulapi │ │ │ └── patterns │ │ │ └── chap4 │ │ │ └── springboot │ │ │ ├── InvestorServicesApplicationTests.java │ │ │ ├── controller │ │ │ └── InvestorControllerTest.java │ │ │ └── service │ │ │ └── InvestorServiceTest.java │ └── circuit-breaker-service │ │ ├── pom.xml │ │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── books │ │ │ │ └── chapters │ │ │ │ └── restfulapi │ │ │ │ └── patterns │ │ │ │ └── chap4 │ │ │ │ └── springboot │ │ │ │ ├── InvestorServicesApplication.java │ │ │ │ ├── controller │ │ │ │ └── InvestorController.java │ │ │ │ ├── models │ │ │ │ ├── IndividualInvestorPortfolio.java │ │ │ │ ├── Investor.java │ │ │ │ ├── Stock.java │ │ │ │ └── errorsandexceptions │ │ │ │ │ └── InvestorNotFoundException.java │ │ │ │ └── service │ │ │ │ └── InvestorService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── books │ │ └── chapters │ │ └── restfulapi │ │ └── patterns │ │ └── chap4 │ │ └── springboot │ │ ├── InvestorServicesApplicationTests.java │ │ ├── controller │ │ └── InvestorControllerTest.java │ │ └── service │ │ └── InvestorServiceTest.java ├── investor-services-auth │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── books │ │ │ └── chapters │ │ │ └── restfulapi │ │ │ └── patterns │ │ │ └── chap4 │ │ │ └── springboot │ │ │ ├── InvestorServicesApplication.java │ │ │ ├── controller │ │ │ └── InvestorController.java │ │ │ ├── models │ │ │ ├── IndividualInvestorPortfolio.java │ │ │ ├── Investor.java │ │ │ ├── Stock.java │ │ │ └── errorsandexceptions │ │ │ │ ├── InvestorNotFoundException.java │ │ │ │ └── VersionNotSupportedException.java │ │ │ ├── security │ │ │ └── PatronsAuthConfig.java │ │ │ └── service │ │ │ └── InvestorService.java │ │ └── test │ │ └── java │ │ └── com │ │ └── books │ │ └── chapters │ │ └── restfulapi │ │ └── patterns │ │ └── chap4 │ │ └── springboot │ │ ├── InvestorServicesApplicationTests.java │ │ ├── controller │ │ └── InvestorControllerTest.java │ │ └── service │ │ └── InvestorServiceTest.java ├── investor-services-bulk │ ├── pom.xml │ ├── scripts │ │ ├── chapter-4-Auth-Collections │ │ └── runMyExamples.bat │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── books │ │ │ └── chapters │ │ │ └── restfulapi │ │ │ └── patterns │ │ │ └── chap4 │ │ │ └── springboot │ │ │ ├── InvestorServicesApplication.java │ │ │ ├── controller │ │ │ ├── InvestorController.java │ │ │ └── InvestorServiceFetchController.java │ │ │ ├── models │ │ │ ├── IndividualInvestorPortfolio.java │ │ │ ├── Investor.java │ │ │ ├── Stock.java │ │ │ └── errorsandexceptions │ │ │ │ ├── CustomHeaderNotFoundException.java │ │ │ │ ├── InvestorNotFoundException.java │ │ │ │ └── VersionNotSupportedException.java │ │ │ ├── security │ │ │ └── PatronsAuthConfig.java │ │ │ └── service │ │ │ ├── InvestorService.java │ │ │ └── InvestorServicesFetchOperations.java │ │ └── test │ │ └── java │ │ └── com │ │ └── books │ │ └── chapters │ │ └── restfulapi │ │ └── patterns │ │ └── chap4 │ │ └── springboot │ │ ├── InvestorServicesApplicationTests.java │ │ ├── controller │ │ └── InvestorControllerTest.java │ │ └── service │ │ ├── InvestorServiceTest.java │ │ └── InvestorServicesFetchOperationsTest.java ├── investor-services-facade │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── books │ │ │ └── chapters │ │ │ └── restfulapi │ │ │ └── patterns │ │ │ └── chap4 │ │ │ └── springboot │ │ │ ├── InvestorServicesApplication.java │ │ │ ├── controller │ │ │ ├── InvestorController.java │ │ │ └── InvestorServiceFetchController.java │ │ │ ├── models │ │ │ ├── IndividualInvestorPortfolio.java │ │ │ ├── Investor.java │ │ │ ├── Stock.java │ │ │ └── errorsandexceptions │ │ │ │ ├── CustomHeaderNotFoundException.java │ │ │ │ ├── InvestorNotFoundException.java │ │ │ │ └── VersionNotSupportedException.java │ │ │ ├── security │ │ │ └── PatronsAuthConfig.java │ │ │ └── service │ │ │ ├── DeleteServiceFacade.java │ │ │ ├── DeleteServiceFacadeImpl.java │ │ │ ├── InvestorService.java │ │ │ └── InvestorServicesFetchOperations.java │ │ └── test │ │ └── java │ │ └── com │ │ └── books │ │ └── chapters │ │ └── restfulapi │ │ └── patterns │ │ └── chap4 │ │ └── springboot │ │ ├── InvestorServicesApplicationTests.java │ │ ├── controller │ │ └── InvestorControllerTest.java │ │ └── service │ │ ├── InvestorServiceTest.java │ │ └── InvestorServicesFetchOperationsTest.java └── investor-services-v2 │ ├── pom.xml │ ├── scripts │ ├── buildMyExamples.bat │ ├── chapter-4-Versioning-Collections │ └── runMyExamples.bat │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── books │ │ │ └── chapters │ │ │ └── restfulapi │ │ │ └── patterns │ │ │ └── chap4 │ │ │ └── springboot │ │ │ ├── InvestorServicesApplication.java │ │ │ ├── controller │ │ │ └── InvestorController.java │ │ │ ├── models │ │ │ ├── IndividualInvestorPortfolio.java │ │ │ ├── Investor.java │ │ │ ├── Stock.java │ │ │ └── errorsandexceptions │ │ │ │ ├── InvestorNotFoundException.java │ │ │ │ └── VersionNotSupportedException.java │ │ │ └── service │ │ │ └── InvestorService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── books │ └── chapters │ └── restfulapi │ └── patterns │ └── chap4 │ └── springboot │ ├── InvestorServicesApplicationTests.java │ ├── controller │ └── InvestorControllerTest.java │ └── service │ └── InvestorServiceTest.java ├── Chapter06 ├── oauth2-sample │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── books │ │ │ │ └── chapters │ │ │ │ └── restfulapi │ │ │ │ └── patterns │ │ │ │ └── advanced │ │ │ │ └── oauth │ │ │ │ ├── AuthorizacionServerConfiguration.java │ │ │ │ ├── ResourceServerConfiguration.java │ │ │ │ ├── WebController.java │ │ │ │ └── WebSecurityConfiguration.java │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── application1.yml │ │ │ └── templates │ │ │ ├── admin.html │ │ │ ├── index.html │ │ │ ├── login.html │ │ │ ├── users.html │ │ │ └── welcome.html │ │ └── test │ │ └── java │ │ └── com │ │ └── books │ │ └── chapters │ │ └── restfulapi │ │ └── patterns │ │ └── advanced │ │ └── oauth │ │ └── WebSecurityConfigurationTests.java └── test.txt ├── Chapter07 └── event-driven-service-orchestration │ ├── amqp-adapter-module │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── books │ │ │ │ └── chapters │ │ │ │ └── restfulapi │ │ │ │ └── patterns │ │ │ │ └── parttwo │ │ │ │ ├── App.java │ │ │ │ └── adapter │ │ │ │ └── amqp │ │ │ │ ├── consumer │ │ │ │ ├── AmqpSubscriber.java │ │ │ │ ├── Configurations.java │ │ │ │ ├── EventHandler.java │ │ │ │ ├── RabbitMqConfig.java │ │ │ │ └── RpcServer.java │ │ │ │ └── producer │ │ │ │ ├── AmqpPublisher.java │ │ │ │ ├── ProducerConfigurations.java │ │ │ │ └── RpcClient.java │ │ └── resources │ │ │ └── schemas │ │ │ ├── BusinessEntity.json │ │ │ ├── ErrorMessage.json │ │ │ ├── ServiceRequest.json │ │ │ └── ServiceResponse.json │ │ └── test │ │ └── java │ │ └── com │ │ └── books │ │ └── chapters │ │ └── restfulapi │ │ └── patterns │ │ └── parttwo │ │ └── AppTest.java │ ├── pom.xml │ ├── service-stubs-module │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── books │ │ │ │ └── chapters │ │ │ │ └── restfulapi │ │ │ │ └── patterns │ │ │ │ └── parttwo │ │ │ │ └── stub │ │ │ │ ├── ServiceStubEventHandler.java │ │ │ │ └── StubServiceApplication.java │ │ └── resources │ │ │ └── application.yaml │ │ └── test │ │ └── java │ │ └── com │ │ └── books │ │ └── chapters │ │ └── restfulapi │ │ └── patterns │ │ └── parttwo │ │ └── AppTest.java │ └── shopping-cart │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── books │ │ │ └── chapters │ │ │ └── restfulapi │ │ │ └── patterns │ │ │ └── parttwo │ │ │ ├── ShoppingCartServiceApp.java │ │ │ ├── bpm │ │ │ ├── AllocateInventoryActivity.java │ │ │ ├── CancelOrderActivity.java │ │ │ ├── CloseShoppingCartActivity.java │ │ │ ├── NotifyCustomerActivity.java │ │ │ ├── PlaceOrderActivity.java │ │ │ ├── ProcessConstants.java │ │ │ ├── ProcessContext.java │ │ │ ├── ProcessUtil.java │ │ │ ├── ReleaseInventoryActivity.java │ │ │ ├── ReleasePaymentActivity.java │ │ │ ├── ReservePaymentActivity.java │ │ │ ├── RetrieveShoppingCartActivity.java │ │ │ ├── SubmitErrorActivity.java │ │ │ └── ValidateAddressActivity.java │ │ │ ├── dataaccess │ │ │ ├── BusinessEntityJpa.java │ │ │ ├── BusinessEntityRepository.java │ │ │ ├── BusinessEntityTranslator.java │ │ │ └── ShoppingCartManager.java │ │ │ └── rest │ │ │ └── ShoppingCartRestController.java │ └── resources │ │ ├── META-INF │ │ └── processes.xml │ │ ├── application.yaml │ │ └── processes │ │ ├── shoppingCartCompensationSubflow.bpmn │ │ └── submitShoppingCart.bpmn │ └── test │ └── java │ └── com │ └── books │ └── chapters │ └── restfulapi │ └── patterns │ └── parttwo │ └── AppTest.java ├── LICENSE ├── README.md ├── investor-services ├── pom.xml ├── scripts │ ├── book-examples.postman_collection.json │ ├── buildMyExamples.bat │ └── runMyExamples.bat └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── books │ │ │ └── chapters │ │ │ └── restfulapi │ │ │ └── patterns │ │ │ └── chap3 │ │ │ └── springboot │ │ │ ├── InvestorServicesApplication.java │ │ │ ├── controller │ │ │ └── InvestorController.java │ │ │ ├── models │ │ │ ├── IndividualInvestorPortfolio.java │ │ │ ├── Investor.java │ │ │ ├── Stock.java │ │ │ └── errorsandexceptions │ │ │ │ └── InvestorNotFoundException.java │ │ │ └── service │ │ │ └── InvestorService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── books │ └── chapters │ └── restfulapi │ └── patterns │ └── chap3 │ └── springboot │ ├── InvestorServicesApplicationTests.java │ ├── controller │ └── InvestorControllerTest.java │ └── service │ └── InvestorServiceTest.java └── scripts ├── book-examples.postman_collection-latest-withOauthSamples.json ├── book-examples.postman_collection.json ├── buildMyExamples.bat └── runMyExamples.bat /Chapter03/investor-services/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.books.chapters.restfulapi.patterns.partOne 7 | investor-services 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | investor-services 12 | sample spring boot application for the the restful API patterns chapter part one 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.4.4.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | com.fasterxml.jackson.dataformat 38 | jackson-dataformat-xml 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-maven-plugin 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Chapter03/investor-services/scripts/buildMyExamples.bat: -------------------------------------------------------------------------------- 1 | mkdir bookexamples 2 | cd bookexamples 3 | mkdir ch03 4 | cd ch03 5 | git clone https://github.com/PacktPublishing/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices.git 6 | cd Hands-On-RESTful-API-Design-Patterns-and-Best-Practices 7 | cd investor-services 8 | mvn clean package -------------------------------------------------------------------------------- /Chapter03/investor-services/scripts/runMyExamples.bat: -------------------------------------------------------------------------------- 1 | java -jar bookexamples/ch03/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices/investor-services/target/investor-services-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /Chapter03/investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/InvestorServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InvestorServicesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(InvestorServicesApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter03/investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/models/IndividualInvestorPortfolio.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.models; 2 | 3 | public class IndividualInvestorPortfolio { 4 | private String investorId; 5 | private int stocksHoldCount; 6 | 7 | public IndividualInvestorPortfolio(String investorId, int stocksHoldCount){ 8 | this.investorId = investorId; 9 | this.stocksHoldCount = stocksHoldCount; 10 | } 11 | 12 | public String getInvestorId() { 13 | return investorId; 14 | } 15 | public void setInvestorId(String investorId) { 16 | this.investorId = investorId; 17 | } 18 | public int getStocksHoldCount() { 19 | return stocksHoldCount; 20 | } 21 | public void setStocksHoldCount(int stocksCount) { 22 | stocksHoldCount = stocksCount; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Chapter03/investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/models/Investor.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.models; 2 | 3 | import java.util.List; 4 | 5 | public class Investor { 6 | private String id; 7 | private String name; 8 | private String description; 9 | private List stocks; 10 | 11 | public Investor(String id, String name, String description, List stocks) { 12 | this.id = id; 13 | this.name = name; 14 | this.description = description; 15 | this.stocks = stocks; 16 | } 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | 42 | public List getStocks() { 43 | return stocks; 44 | } 45 | 46 | public void setStocks(List stocks) { 47 | this.stocks = stocks; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return String.format("Investor [id=%s, name=%s, description=%s, stocks=%s]", id, name, description, stocks); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter03/investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/models/Stock.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.models; 2 | 3 | public class Stock { 4 | private String symbol; 5 | private int numberOfSharesHeld; 6 | private double tickerPrice = 0.0; 7 | 8 | Stock() { 9 | // default constructor, otherwise the spring boot runtime complains that 10 | // it can not instantiate this Stock class when we try to do POST to 11 | // insert a new Stock from the controller classes 12 | } 13 | 14 | public Stock(String symbol, int numberOfSharesHeld, double tickerPrice) { 15 | 16 | this.symbol = symbol; 17 | this.numberOfSharesHeld = numberOfSharesHeld; 18 | this.tickerPrice = tickerPrice; 19 | } 20 | 21 | public String getSymbol() { 22 | return symbol; 23 | } 24 | 25 | public void setSymbol(String symbol) { 26 | this.symbol = symbol; 27 | } 28 | 29 | public int getNumberOfSharesHeld() { 30 | return numberOfSharesHeld; 31 | } 32 | 33 | public void setNumberOfSharesHeld(int numberOfShares) { 34 | this.numberOfSharesHeld = numberOfShares; 35 | } 36 | 37 | public double getTickerPrice() { 38 | return tickerPrice; 39 | } 40 | 41 | public void setTickerPrice(double tickerPrice) { 42 | this.tickerPrice = tickerPrice; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | String pattern = "Stock [symbol: %s, numberOfSharesHeld: %d, tickerPrice: %6.2f]"; 48 | return String.format(pattern, symbol, numberOfSharesHeld, tickerPrice); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Chapter03/investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/models/errorsandexceptions/InvestorNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.models.errorsandexceptions; 2 | 3 | public class InvestorNotFoundException extends RuntimeException{ 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public InvestorNotFoundException(String exception) 8 | { 9 | super(exception); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter03/investor-services/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=9090 -------------------------------------------------------------------------------- /Chapter03/investor-services/src/test/java/com/books/chapters/restfulapi/patterns/chap3/springboot/InvestorServicesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class InvestorServicesApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter03/investor-services/src/test/java/com/books/chapters/restfulapi/patterns/chap3/springboot/controller/InvestorControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.controller; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.mock.web.MockHttpServletResponse; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.MvcResult; 13 | import org.springframework.test.web.servlet.RequestBuilder; 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 15 | 16 | import com.books.chapters.restfulapi.patterns.chap3.springboot.controller.InvestorController; 17 | import com.books.chapters.restfulapi.patterns.chap3.springboot.service.InvestorService; 18 | 19 | @RunWith(SpringRunner.class) 20 | @WebMvcTest(value = InvestorController.class, secure = false) 21 | public class InvestorControllerTest { 22 | 23 | @Autowired 24 | private MockMvc mockMvc; 25 | 26 | @MockBean 27 | private InvestorService investorService; 28 | 29 | @Test 30 | public void fetchAllInvestors() throws Exception{ 31 | RequestBuilder requestBuilder = MockMvcRequestBuilders.get( 32 | "/investors").accept( 33 | MediaType.APPLICATION_JSON); 34 | MvcResult result = mockMvc.perform(requestBuilder).andReturn(); 35 | MockHttpServletResponse response = result.getResponse(); 36 | System.out.println("here "+response); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.books.chapters.restfulapi.patterns.partOne 7 | circuit-breaker-service-consumer 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | investor-services 12 | sample consumer sevice of circuit breaker implementation 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | com.fasterxml.jackson.dataformat 38 | jackson-dataformat-xml 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-starter-netflix-hystrix 48 | 2.0.1.RELEASE 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InvestorServicesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(InvestorServicesApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorController.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 13 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 14 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions.InvestorNotFoundException; 15 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 16 | 17 | @EnableCircuitBreaker 18 | @RestController 19 | public class InvestorController { 20 | 21 | @Autowired 22 | private InvestorService investorService; 23 | 24 | 25 | // call the downstream service circuit-breaker-service 26 | @GetMapping(value="/welcome", produces="text/plain;charset=UTF-8") 27 | public String welcomePageWhichProducesCharset() { 28 | return investorService.circuitBreakerImplWelcome(); 29 | } 30 | 31 | 32 | @GetMapping("/investors/{investorId}") 33 | public Investor fetchInvestorById(@PathVariable String investorId) { 34 | Investor resultantInvestor = investorService.fetchInvestorById(investorId); 35 | if (resultantInvestor == null) { 36 | throw new InvestorNotFoundException("Investor Id-" + investorId); 37 | } 38 | return resultantInvestor; 39 | } 40 | 41 | @GetMapping(path = "/investors/{investorId}/stocks") 42 | public List fetchStocksByInvestorId(@PathVariable String investorId, 43 | @RequestParam(value = "offset", defaultValue = "0") int offset, 44 | @RequestParam(value = "limit", defaultValue = "5") int limit) { 45 | return investorService.fetchStocksByInvestorId(investorId, offset, limit); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/IndividualInvestorPortfolio.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class IndividualInvestorPortfolio { 4 | private String investorId; 5 | private int stocksHoldCount; 6 | 7 | public IndividualInvestorPortfolio(String investorId, int stocksHoldCount){ 8 | this.investorId = investorId; 9 | this.stocksHoldCount = stocksHoldCount; 10 | } 11 | 12 | public String getInvestorId() { 13 | return investorId; 14 | } 15 | public void setInvestorId(String investorId) { 16 | this.investorId = investorId; 17 | } 18 | public int getStocksHoldCount() { 19 | return stocksHoldCount; 20 | } 21 | public void setStocksHoldCount(int stocksCount) { 22 | stocksHoldCount = stocksCount; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Investor.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | import java.util.List; 4 | 5 | public class Investor { 6 | private String id; 7 | private String name; 8 | private String description; 9 | private List stocks; 10 | 11 | public Investor(String id, String name, String description, List stocks) { 12 | this.id = id; 13 | this.name = name; 14 | this.description = description; 15 | this.stocks = stocks; 16 | } 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | 42 | public List getStocks() { 43 | return stocks; 44 | } 45 | 46 | public void setStocks(List stocks) { 47 | this.stocks = stocks; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return String.format("Investor [id=%s, name=%s, description=%s, stocks=%s]", id, name, description, stocks); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Stock.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class Stock { 4 | private String symbol; 5 | private int numberOfSharesHeld; 6 | private double tickerPrice = 0.0; 7 | 8 | Stock() { 9 | // default constructor, otherwise the spring boot runtime complains that 10 | // it can not instantiate this Stock class when we try to do POST to 11 | // insert a new Stock from the controller classes 12 | } 13 | 14 | public Stock(String symbol, int numberOfSharesHeld, double tickerPrice) { 15 | 16 | this.symbol = symbol; 17 | this.numberOfSharesHeld = numberOfSharesHeld; 18 | this.tickerPrice = tickerPrice; 19 | } 20 | 21 | public String getSymbol() { 22 | return symbol; 23 | } 24 | 25 | public void setSymbol(String symbol) { 26 | this.symbol = symbol; 27 | } 28 | 29 | public int getNumberOfSharesHeld() { 30 | return numberOfSharesHeld; 31 | } 32 | 33 | public void setNumberOfSharesHeld(int numberOfShares) { 34 | this.numberOfSharesHeld = numberOfShares; 35 | } 36 | 37 | public double getTickerPrice() { 38 | return tickerPrice; 39 | } 40 | 41 | public void setTickerPrice(double tickerPrice) { 42 | this.tickerPrice = tickerPrice; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | String pattern = "Stock [symbol: %s, numberOfSharesHeld: %d, tickerPrice: %6.2f]"; 48 | return String.format(pattern, symbol, numberOfSharesHeld, tickerPrice); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/InvestorNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class InvestorNotFoundException extends RuntimeException{ 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public InvestorNotFoundException(String exception) 8 | { 9 | super(exception); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=9090 -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class InvestorServicesApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.mock.web.MockHttpServletResponse; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.MvcResult; 13 | import org.springframework.test.web.servlet.RequestBuilder; 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 15 | 16 | import com.books.chapters.restfulapi.patterns.chap4.springboot.controller.InvestorController; 17 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 18 | 19 | @RunWith(SpringRunner.class) 20 | @WebMvcTest(value = InvestorController.class, secure = false) 21 | public class InvestorControllerTest { 22 | 23 | @Autowired 24 | private MockMvc mockMvc; 25 | 26 | @MockBean 27 | private InvestorService investorService; 28 | 29 | @Test 30 | public void fetchAllInvestors() throws Exception{ 31 | RequestBuilder requestBuilder = MockMvcRequestBuilders.get( 32 | "/investors").accept( 33 | MediaType.APPLICATION_JSON); 34 | MvcResult result = mockMvc.perform(requestBuilder).andReturn(); 35 | MockHttpServletResponse response = result.getResponse(); 36 | System.out.println("here "+response); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service-consumer/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/InvestorServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.junit.Test; 7 | 8 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 9 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 10 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 11 | 12 | import junit.framework.TestCase; 13 | 14 | public class InvestorServiceTest extends TestCase { 15 | 16 | @Override 17 | protected void setUp() throws Exception { 18 | 19 | } 20 | 21 | @Test 22 | public void testFetchAllInvestors() { 23 | int expectedSize = 2; 24 | int actualListSize = getInvestorServiceForTest().fetchAllInvestors().size(); 25 | assertEquals(expectedSize, actualListSize); 26 | } 27 | 28 | @Test 29 | public void testFetchInvestorById() { 30 | Investor actualInvestor = getInvestorServiceForTest().fetchInvestorById("invr_1"); 31 | Investor expectedInvestor = new Investor("INVR_1", "Investor ONE", "conservative investor", getStocksSetOne()); 32 | assertEquals(actualInvestor.getId(), expectedInvestor.getId()); 33 | actualInvestor = getInvestorServiceForTest().fetchInvestorById("invr_2"); 34 | expectedInvestor = new Investor("INVR_2", "Investor TWO", "Moderate Risk investor", getStocksSetTwo()); 35 | assertEquals(expectedInvestor.getId(), actualInvestor.getId()); 36 | 37 | } 38 | 39 | @Test 40 | public void testFetchStocksByInvestorId() { 41 | Investor expectedInvestor = getInvestorServiceForTest().fetchInvestorById("invr_1"); 42 | String expectedStockSymbol = "EXB"; 43 | assertEquals(expectedInvestor.getStocks().size(), 4); 44 | Stock expectedStock = expectedInvestor.getStocks().stream() 45 | .filter(stock -> expectedStockSymbol.equalsIgnoreCase(stock.getSymbol())).findAny().orElse(null); 46 | assertNotNull(expectedStock); 47 | 48 | } 49 | 50 | @Test 51 | public void testFetchSingleStockByInvestorIdAndStockSymbol() { 52 | String investorId = "Invr_1"; 53 | String expectedSymbol = "EXA"; 54 | String actualSymbol = (getInvestorServiceForTest() 55 | .fetchSingleStockByInvestorIdAndStockSymbol(investorId, expectedSymbol).getSymbol()); 56 | assertEquals(expectedSymbol, actualSymbol); 57 | } 58 | 59 | private InvestorService getInvestorServiceForTest() { 60 | return new InvestorService(); 61 | } 62 | 63 | private List getStocksSetOne() { 64 | ArrayList stocksLotOne = new ArrayList<>(); 65 | Stock stocksSampleOne = new Stock("EXA", 200, 20); 66 | Stock stocksSampleTwo = new Stock("EXB", 100, 60); 67 | 68 | stocksLotOne.add(stocksSampleTwo); 69 | stocksLotOne.add(stocksSampleOne); 70 | return stocksLotOne; 71 | } 72 | 73 | private List getStocksSetTwo() { 74 | ArrayList stocksLotTwo = new ArrayList<>(); 75 | Stock stocksSampleThree = new Stock("EXC", 300, 200); 76 | Stock stocksSampleFour = new Stock("EXD", 150, 40); 77 | 78 | stocksLotTwo.add(stocksSampleThree); 79 | stocksLotTwo.add(stocksSampleFour); 80 | return stocksLotTwo; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.books.chapters.restfulapi.patterns.partOne 7 | circuit-breaker-service 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | investor-services 12 | circuit breaker implementation 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | com.fasterxml.jackson.dataformat 38 | jackson-dataformat-xml 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-maven-plugin 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InvestorServicesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(InvestorServicesApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorController.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 12 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 13 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions.InvestorNotFoundException; 14 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 15 | 16 | @RestController 17 | public class InvestorController { 18 | 19 | @Autowired 20 | private InvestorService investorService; 21 | 22 | @GetMapping(value = "/investors/welcome", produces = "text/plain;charset=UTF-8") 23 | public String welcomePageWhichProducesCharset() { 24 | return investorService.welcomePage(); 25 | } 26 | 27 | @GetMapping("/investors/{investorId}") 28 | public Investor fetchInvestorById(@PathVariable String investorId) { 29 | Investor resultantInvestor = investorService.fetchInvestorById(investorId); 30 | if (resultantInvestor == null) { 31 | throw new InvestorNotFoundException("Investor Id-" + investorId); 32 | } 33 | return resultantInvestor; 34 | } 35 | 36 | @GetMapping(path = "/investors/{investorId}/stocks") 37 | public List fetchStocksByInvestorId(@PathVariable String investorId, 38 | @RequestParam(value = "offset", defaultValue = "0") int offset, 39 | @RequestParam(value = "limit", defaultValue = "5") int limit) { 40 | return investorService.fetchStocksByInvestorId(investorId, offset, limit); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/IndividualInvestorPortfolio.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class IndividualInvestorPortfolio { 4 | private String investorId; 5 | private int stocksHoldCount; 6 | 7 | public IndividualInvestorPortfolio(String investorId, int stocksHoldCount){ 8 | this.investorId = investorId; 9 | this.stocksHoldCount = stocksHoldCount; 10 | } 11 | 12 | public String getInvestorId() { 13 | return investorId; 14 | } 15 | public void setInvestorId(String investorId) { 16 | this.investorId = investorId; 17 | } 18 | public int getStocksHoldCount() { 19 | return stocksHoldCount; 20 | } 21 | public void setStocksHoldCount(int stocksCount) { 22 | stocksHoldCount = stocksCount; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Investor.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | import java.util.List; 4 | 5 | public class Investor { 6 | private String id; 7 | private String name; 8 | private String description; 9 | private List stocks; 10 | 11 | public Investor(String id, String name, String description, List stocks) { 12 | this.id = id; 13 | this.name = name; 14 | this.description = description; 15 | this.stocks = stocks; 16 | } 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | 42 | public List getStocks() { 43 | return stocks; 44 | } 45 | 46 | public void setStocks(List stocks) { 47 | this.stocks = stocks; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return String.format("Investor [id=%s, name=%s, description=%s, stocks=%s]", id, name, description, stocks); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Stock.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class Stock { 4 | private String symbol; 5 | private int numberOfSharesHeld; 6 | private double tickerPrice = 0.0; 7 | 8 | Stock() { 9 | // default constructor, otherwise the spring boot runtime complains that 10 | // it can not instantiate this Stock class when we try to do POST to 11 | // insert a new Stock from the controller classes 12 | } 13 | 14 | public Stock(String symbol, int numberOfSharesHeld, double tickerPrice) { 15 | 16 | this.symbol = symbol; 17 | this.numberOfSharesHeld = numberOfSharesHeld; 18 | this.tickerPrice = tickerPrice; 19 | } 20 | 21 | public String getSymbol() { 22 | return symbol; 23 | } 24 | 25 | public void setSymbol(String symbol) { 26 | this.symbol = symbol; 27 | } 28 | 29 | public int getNumberOfSharesHeld() { 30 | return numberOfSharesHeld; 31 | } 32 | 33 | public void setNumberOfSharesHeld(int numberOfShares) { 34 | this.numberOfSharesHeld = numberOfShares; 35 | } 36 | 37 | public double getTickerPrice() { 38 | return tickerPrice; 39 | } 40 | 41 | public void setTickerPrice(double tickerPrice) { 42 | this.tickerPrice = tickerPrice; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | String pattern = "Stock [symbol: %s, numberOfSharesHeld: %d, tickerPrice: %6.2f]"; 48 | return String.format(pattern, symbol, numberOfSharesHeld, tickerPrice); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/InvestorNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class InvestorNotFoundException extends RuntimeException{ 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public InvestorNotFoundException(String exception) 8 | { 9 | super(exception); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/InvestorService.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 11 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 12 | 13 | @Component 14 | public class InvestorService { 15 | 16 | private static final String WELCOME_INVESTOR_IN_JAPANESE = "ウェルカムインベスター (\"Welcome Investor!\" in Japanese)"; 17 | 18 | private static List investorsList = new ArrayList<>(); 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(InvestorService.class); 21 | 22 | static { 23 | // create some data 24 | Stock stocksSampleOne = new Stock("EXA", 200, 20); 25 | Stock stocksSampleTwo = new Stock("EXB", 100, 60); 26 | 27 | Stock stocksSampleThree = new Stock("EXC", 300, 200); 28 | Stock stocksSampleFour = new Stock("EXD", 150, 40); 29 | 30 | Stock stockSampleFive = new Stock("EX5", 200, 20); 31 | Stock stockSampleSix = new Stock("EX6", 200, 20); 32 | 33 | ArrayList stocksLotOne = new ArrayList<>(); 34 | stocksLotOne.add(stocksSampleOne); 35 | stocksLotOne.add(stocksSampleTwo); 36 | stocksLotOne.add(stockSampleFive); 37 | stocksLotOne.add(stockSampleSix); 38 | ArrayList stocksLotTwo = new ArrayList<>(); 39 | stocksLotTwo.add(stocksSampleThree); 40 | stocksLotTwo.add(stocksSampleFour); 41 | stocksLotTwo.add(stockSampleFive); 42 | stocksLotTwo.add(stockSampleSix); 43 | 44 | Investor investorOne = new Investor("INVR_1", "Investor ONE", "conservative investor", stocksLotOne); 45 | Investor investorTwo = new Investor("INVR_2", "Investor TWO", "Moderate Risk investor", stocksLotTwo); 46 | 47 | investorsList.add(investorOne); 48 | investorsList.add(investorTwo); 49 | 50 | } 51 | 52 | public List fetchAllInvestors() { 53 | return investorsList; 54 | } 55 | 56 | public Investor fetchInvestorById(String investorId) { 57 | return investorsList.stream().filter(investors -> investorId.equalsIgnoreCase(investors.getId())).findAny() 58 | .orElse(null); 59 | 60 | } 61 | 62 | public List fetchStocksByInvestorId(String investorId, int offset, int limit) { 63 | Investor investor = fetchInvestorById(investorId); 64 | return investor.getStocks().subList(getStartFrom(offset, investor), getToIndex(offset, limit, investor)); 65 | } 66 | 67 | private int getToIndex(int offset, int limit, Investor investor) { 68 | int toIndex = offset + limit; 69 | return (toIndex) > investor.getStocks().size() ? investor.getStocks().size() : toIndex; 70 | } 71 | 72 | private int getStartFrom(int offset, Investor investor) { 73 | return (offset) >= investor.getStocks().size() ? investor.getStocks().size() : offset; 74 | } 75 | 76 | public Stock fetchSingleStockByInvestorIdAndStockSymbol(String investorId, String symbol) { 77 | Investor investor = fetchInvestorById(investorId); 78 | return investor.getStocks().stream().filter(stock -> symbol.equalsIgnoreCase(stock.getSymbol())).findAny() 79 | .orElse(null); 80 | } 81 | 82 | public String welcomePage() { 83 | logger.info("reached circuit breaker service welcome page"); 84 | return WELCOME_INVESTOR_IN_JAPANESE; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8090 -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class InvestorServicesApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.mock.web.MockHttpServletResponse; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.MvcResult; 13 | import org.springframework.test.web.servlet.RequestBuilder; 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 15 | 16 | import com.books.chapters.restfulapi.patterns.chap4.springboot.controller.InvestorController; 17 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 18 | 19 | @RunWith(SpringRunner.class) 20 | @WebMvcTest(value = InvestorController.class, secure = false) 21 | public class InvestorControllerTest { 22 | 23 | @Autowired 24 | private MockMvc mockMvc; 25 | 26 | @MockBean 27 | private InvestorService investorService; 28 | 29 | @Test 30 | public void fetchAllInvestors() throws Exception{ 31 | RequestBuilder requestBuilder = MockMvcRequestBuilders.get( 32 | "/investors").accept( 33 | MediaType.APPLICATION_JSON); 34 | MvcResult result = mockMvc.perform(requestBuilder).andReturn(); 35 | MockHttpServletResponse response = result.getResponse(); 36 | System.out.println("here "+response); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter04/circuit-breaker-examples/circuit-breaker-service/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/InvestorServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.junit.Test; 7 | 8 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 9 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 10 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 11 | 12 | import junit.framework.TestCase; 13 | 14 | public class InvestorServiceTest extends TestCase { 15 | 16 | @Override 17 | protected void setUp() throws Exception { 18 | 19 | } 20 | 21 | @Test 22 | public void testFetchAllInvestors() { 23 | int expectedSize = 2; 24 | int actualListSize = getInvestorServiceForTest().fetchAllInvestors().size(); 25 | assertEquals(expectedSize, actualListSize); 26 | } 27 | 28 | @Test 29 | public void testFetchInvestorById() { 30 | Investor actualInvestor = getInvestorServiceForTest().fetchInvestorById("invr_1"); 31 | Investor expectedInvestor = new Investor("INVR_1", "Investor ONE", "conservative investor", getStocksSetOne()); 32 | assertEquals(actualInvestor.getId(), expectedInvestor.getId()); 33 | actualInvestor = getInvestorServiceForTest().fetchInvestorById("invr_2"); 34 | expectedInvestor = new Investor("INVR_2", "Investor TWO", "Moderate Risk investor", getStocksSetTwo()); 35 | assertEquals(expectedInvestor.getId(), actualInvestor.getId()); 36 | 37 | } 38 | 39 | @Test 40 | public void testFetchStocksByInvestorId() { 41 | Investor expectedInvestor = getInvestorServiceForTest().fetchInvestorById("invr_1"); 42 | String expectedStockSymbol = "EXB"; 43 | assertEquals(expectedInvestor.getStocks().size(), 4); 44 | Stock expectedStock = expectedInvestor.getStocks().stream() 45 | .filter(stock -> expectedStockSymbol.equalsIgnoreCase(stock.getSymbol())).findAny().orElse(null); 46 | assertNotNull(expectedStock); 47 | 48 | } 49 | 50 | @Test 51 | public void testFetchSingleStockByInvestorIdAndStockSymbol() { 52 | String investorId = "Invr_1"; 53 | String expectedSymbol = "EXA"; 54 | String actualSymbol = (getInvestorServiceForTest() 55 | .fetchSingleStockByInvestorIdAndStockSymbol(investorId, expectedSymbol).getSymbol()); 56 | assertEquals(expectedSymbol, actualSymbol); 57 | } 58 | 59 | private InvestorService getInvestorServiceForTest() { 60 | return new InvestorService(); 61 | } 62 | 63 | private List getStocksSetOne() { 64 | ArrayList stocksLotOne = new ArrayList<>(); 65 | Stock stocksSampleOne = new Stock("EXA", 200, 20); 66 | Stock stocksSampleTwo = new Stock("EXB", 100, 60); 67 | 68 | stocksLotOne.add(stocksSampleTwo); 69 | stocksLotOne.add(stocksSampleOne); 70 | return stocksLotOne; 71 | } 72 | 73 | private List getStocksSetTwo() { 74 | ArrayList stocksLotTwo = new ArrayList<>(); 75 | Stock stocksSampleThree = new Stock("EXC", 300, 200); 76 | Stock stocksSampleFour = new Stock("EXD", 150, 40); 77 | 78 | stocksLotTwo.add(stocksSampleThree); 79 | stocksLotTwo.add(stocksSampleFour); 80 | return stocksLotTwo; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.books.chapters.restfulapi.patterns.partOne 7 | investor-services 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | investor-services-v2 12 | sample spring boot application for the the restful API patterns chapter part two 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.4.4.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-security 39 | 40 | 41 | com.fasterxml.jackson.dataformat 42 | jackson-dataformat-xml 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InvestorServicesApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(InvestorServicesApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/IndividualInvestorPortfolio.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class IndividualInvestorPortfolio { 4 | private String investorId; 5 | private int stocksHoldCount; 6 | 7 | public IndividualInvestorPortfolio(String investorId, int stocksHoldCount){ 8 | this.investorId = investorId; 9 | this.stocksHoldCount = stocksHoldCount; 10 | } 11 | 12 | public String getInvestorId() { 13 | return investorId; 14 | } 15 | public void setInvestorId(String investorId) { 16 | this.investorId = investorId; 17 | } 18 | public int getStocksHoldCount() { 19 | return stocksHoldCount; 20 | } 21 | public void setStocksHoldCount(int stocksCount) { 22 | stocksHoldCount = stocksCount; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Investor.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | import java.util.List; 4 | 5 | public class Investor { 6 | private String id; 7 | private String name; 8 | private String description; 9 | private List stocks; 10 | 11 | public Investor(String id, String name, String description, List stocks) { 12 | this.id = id; 13 | this.name = name; 14 | this.description = description; 15 | this.stocks = stocks; 16 | } 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | 42 | public List getStocks() { 43 | return stocks; 44 | } 45 | 46 | public void setStocks(List stocks) { 47 | this.stocks = stocks; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return String.format("Investor [id=%s, name=%s, description=%s, stocks=%s]", id, name, description, stocks); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Stock.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class Stock { 4 | private String symbol; 5 | private int numberOfSharesHeld; 6 | private double tickerPrice = 0.0; 7 | 8 | Stock() { 9 | // default constructor, otherwise the spring boot runtime complains that 10 | // it can not instantiate this Stock class when we try to do POST to 11 | // insert a new Stock from the controller classes 12 | } 13 | 14 | public Stock(String symbol, int numberOfSharesHeld, double tickerPrice) { 15 | 16 | this.symbol = symbol; 17 | this.numberOfSharesHeld = numberOfSharesHeld; 18 | this.tickerPrice = tickerPrice; 19 | } 20 | 21 | public String getSymbol() { 22 | return symbol; 23 | } 24 | 25 | public void setSymbol(String symbol) { 26 | this.symbol = symbol; 27 | } 28 | 29 | public int getNumberOfSharesHeld() { 30 | return numberOfSharesHeld; 31 | } 32 | 33 | public void setNumberOfSharesHeld(int numberOfShares) { 34 | this.numberOfSharesHeld = numberOfShares; 35 | } 36 | 37 | public double getTickerPrice() { 38 | return tickerPrice; 39 | } 40 | 41 | public void setTickerPrice(double tickerPrice) { 42 | this.tickerPrice = tickerPrice; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | String pattern = "Stock [symbol: %s, numberOfSharesHeld: %d, tickerPrice: %6.2f]"; 48 | return String.format(pattern, symbol, numberOfSharesHeld, tickerPrice); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/InvestorNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class InvestorNotFoundException extends RuntimeException{ 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public InvestorNotFoundException(String exception) 8 | { 9 | super(exception); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/VersionNotSupportedException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class VersionNotSupportedException extends Exception { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public VersionNotSupportedException(String exception) 7 | { 8 | super(exception); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/security/PatronsAuthConfig.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.security; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 8 | import org.springframework.security.config.http.SessionCreationPolicy; 9 | 10 | @Configuration 11 | @EnableWebSecurity 12 | public class PatronsAuthConfig extends WebSecurityConfigurerAdapter { 13 | 14 | private static final String NO_RESTRICT_WELCOME_URI = "/welcome"; 15 | private static final String DEFAULT_ADMIN_SECRET = "admSecret"; 16 | private static final String DEFAULT_ADMIN_ID = "admin"; 17 | private static final String DEFAULT_USER_ROLE = "USER"; 18 | private static final String DEFAULT_ADMIN_ROLE = "ADMIN"; 19 | private static final String DEFAULT_USER_SECRET = "usrSecret"; 20 | private static final String DEFAULT_USER_ID = "user"; 21 | 22 | @Override 23 | protected void configure(AuthenticationManagerBuilder authMgrBldr) throws Exception { 24 | authMgrBldr.inMemoryAuthentication() 25 | .passwordEncoder(org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()) 26 | .withUser(DEFAULT_USER_ID).password(DEFAULT_USER_SECRET).authorities(DEFAULT_USER_ROLE).and() 27 | .withUser(DEFAULT_ADMIN_ID).password(DEFAULT_ADMIN_SECRET) 28 | .authorities(DEFAULT_USER_ROLE, DEFAULT_ADMIN_ROLE); 29 | } 30 | 31 | // for any URL applying the security (basic authentication) 32 | @Override 33 | protected void configure(HttpSecurity httpSec) throws Exception { 34 | // force for credentials every time (stateless) 35 | httpSec.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 36 | // Invalid CSRF Token 'null' was found on the request parameter '_csrf' 37 | // or header 'X-CSRF-TOKEN' 38 | httpSec.csrf().disable(); 39 | 40 | // welcome page no authentication required 41 | // /investors/admin only admin should be able to access 42 | // other pages Users should also be able to access 43 | httpSec 44 | .csrf().disable() 45 | .authorizeRequests() 46 | .and() 47 | .authorizeRequests() 48 | .antMatchers(NO_RESTRICT_WELCOME_URI).permitAll() 49 | .antMatchers("/investors/admin").hasAuthority(DEFAULT_ADMIN_ROLE) 50 | .antMatchers("/investors/invr*/**").access("hasAuthority('"+DEFAULT_USER_ROLE+"')") 51 | .anyRequest().authenticated() 52 | .and() 53 | .httpBasic() 54 | .and() 55 | .logout(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class InvestorServicesApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/investor-services-auth/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.mock.web.MockHttpServletResponse; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.MvcResult; 13 | import org.springframework.test.web.servlet.RequestBuilder; 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 15 | 16 | import com.books.chapters.restfulapi.patterns.chap4.springboot.controller.InvestorController; 17 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 18 | 19 | @RunWith(SpringRunner.class) 20 | @WebMvcTest(value = InvestorController.class, secure = false) 21 | public class InvestorControllerTest { 22 | 23 | @Autowired 24 | private MockMvc mockMvc; 25 | 26 | @MockBean 27 | private InvestorService investorServices; 28 | 29 | @Test 30 | public void fetchAllInvestors() throws Exception{ 31 | RequestBuilder requestBuilder = MockMvcRequestBuilders.get( 32 | "/investors").accept( 33 | MediaType.APPLICATION_JSON); 34 | MvcResult result = mockMvc.perform(requestBuilder).andReturn(); 35 | MockHttpServletResponse response = result.getResponse(); 36 | System.out.println("here "+response); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.books.chapters.restfulapi.patterns.partOne 7 | investor-services 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | investor-services-v2 12 | sample spring boot application for the the restful API patterns chapter part two 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.4.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-security 39 | 40 | 41 | com.fasterxml.jackson.dataformat 42 | jackson-dataformat-xml 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/scripts/runMyExamples.bat: -------------------------------------------------------------------------------- 1 | java -jar bookexamples/ch04-auth/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices/investor-services/target/investor-services-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InvestorServicesApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(InvestorServicesApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorServiceFetchController.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestParam; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 14 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 15 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions.InvestorNotFoundException; 16 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorServicesFetchOperations; 17 | 18 | @RestController 19 | @ComponentScan(basePackageClasses = { InvestorServicesFetchOperations.class }) 20 | public class InvestorServiceFetchController { 21 | @Autowired 22 | private InvestorServicesFetchOperations investorServiceFetchOpertions; 23 | 24 | @GetMapping(value = "/welcome", produces = "text/plain;charset=UTF-8") 25 | public String welcomePageWhichProducesCharset() { 26 | return "ウェルカムインベスター (\"Welcome Investor!\" in Japanese)"; 27 | } 28 | 29 | @GetMapping("/investors") 30 | public String welcomeUsers() { 31 | return "You are in right API, However please refer the API docs for specific URLs that can be accessed with your Roles"; 32 | } 33 | 34 | @GetMapping("/investors/admin") 35 | public List fetchAllInvestors() { 36 | return investorServiceFetchOpertions.fetchAllInvestors(); 37 | } 38 | 39 | @GetMapping("/investors/{investorId}") 40 | public Investor fetchInvestorById(@PathVariable String investorId) { 41 | Investor resultantInvestor = investorServiceFetchOpertions.fetchInvestorById(investorId); 42 | if (resultantInvestor == null) { 43 | throw new InvestorNotFoundException("Investor Id-" + investorId); 44 | } 45 | return resultantInvestor; 46 | } 47 | 48 | @GetMapping(path = "/investors/{investorId}/stocks") 49 | public List fetchStocksByInvestorId(@PathVariable String investorId, 50 | @RequestParam(value = "offset", defaultValue = "0") int offset, 51 | @RequestParam(value = "limit", defaultValue = "5") int limit) { 52 | return investorServiceFetchOpertions.fetchStocksByInvestorId(investorId, offset, limit); 53 | } 54 | 55 | /** 56 | * method example which produces both xml and json output, other methods 57 | * produces only json response and for other content type it errors out 406 58 | * content not allowed 59 | */ 60 | @GetMapping(path = "/investors/{investorId}/stocks/{symbol}", produces = { MediaType.APPLICATION_JSON_VALUE, 61 | MediaType.APPLICATION_XML_VALUE }) 62 | public Stock fetchAStockByInvestorIdAndStockId(@PathVariable String investorId, @PathVariable String symbol) { 63 | return investorServiceFetchOpertions.fetchSingleStockByInvestorIdAndStockSymbol(investorId, symbol); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/IndividualInvestorPortfolio.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class IndividualInvestorPortfolio { 4 | private String investorId; 5 | private int stocksHoldCount; 6 | 7 | public IndividualInvestorPortfolio(String investorId, int stocksHoldCount){ 8 | this.investorId = investorId; 9 | this.stocksHoldCount = stocksHoldCount; 10 | } 11 | 12 | public String getInvestorId() { 13 | return investorId; 14 | } 15 | public void setInvestorId(String investorId) { 16 | this.investorId = investorId; 17 | } 18 | public int getStocksHoldCount() { 19 | return stocksHoldCount; 20 | } 21 | public void setStocksHoldCount(int stocksCount) { 22 | stocksHoldCount = stocksCount; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Investor.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | import java.util.List; 4 | 5 | public class Investor { 6 | private String id; 7 | private String name; 8 | private String description; 9 | private List stocks; 10 | 11 | public Investor(String id, String name, String description, List stocks) { 12 | this.id = id; 13 | this.name = name; 14 | this.description = description; 15 | this.stocks = stocks; 16 | } 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | 42 | public List getStocks() { 43 | return stocks; 44 | } 45 | 46 | public void setStocks(List stocks) { 47 | this.stocks = stocks; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return String.format("Investor [id=%s, name=%s, description=%s, stocks=%s]", id, name, description, stocks); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Stock.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class Stock { 4 | private String symbol; 5 | private int numberOfSharesHeld; 6 | private double tickerPrice = 0.0; 7 | 8 | Stock() { 9 | // default constructor, otherwise the spring boot runtime complains that 10 | // it can not instantiate this Stock class when we try to do POST to 11 | // insert a new Stock from the controller classes 12 | } 13 | 14 | public Stock(String symbol, int numberOfSharesHeld, double tickerPrice) { 15 | 16 | this.symbol = symbol; 17 | this.numberOfSharesHeld = numberOfSharesHeld; 18 | this.tickerPrice = tickerPrice; 19 | } 20 | 21 | public String getSymbol() { 22 | return symbol; 23 | } 24 | 25 | public void setSymbol(String symbol) { 26 | this.symbol = symbol; 27 | } 28 | 29 | public int getNumberOfSharesHeld() { 30 | return numberOfSharesHeld; 31 | } 32 | 33 | public void setNumberOfSharesHeld(int numberOfShares) { 34 | this.numberOfSharesHeld = numberOfShares; 35 | } 36 | 37 | public double getTickerPrice() { 38 | return tickerPrice; 39 | } 40 | 41 | public void setTickerPrice(double tickerPrice) { 42 | this.tickerPrice = tickerPrice; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | String pattern = "Stock [symbol: %s, numberOfSharesHeld: %d, tickerPrice: %6.2f]"; 48 | return String.format(pattern, symbol, numberOfSharesHeld, tickerPrice); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/CustomHeaderNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class CustomHeaderNotFoundException extends Exception { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public CustomHeaderNotFoundException(String exception) { 7 | super(exception); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/InvestorNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class InvestorNotFoundException extends RuntimeException{ 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public InvestorNotFoundException(String exception) 8 | { 9 | super(exception); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/VersionNotSupportedException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class VersionNotSupportedException extends Exception { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public VersionNotSupportedException(String exception) 7 | { 8 | super(exception); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/security/PatronsAuthConfig.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.security; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 8 | import org.springframework.security.config.http.SessionCreationPolicy; 9 | 10 | @Configuration 11 | @EnableWebSecurity 12 | public class PatronsAuthConfig extends WebSecurityConfigurerAdapter { 13 | 14 | private static final String NO_RESTRICT_WELCOME_URI = "/welcome"; 15 | private static final String DEFAULT_ADMIN_SECRET = "admSecret"; 16 | private static final String DEFAULT_ADMIN_ID = "admin"; 17 | private static final String DEFAULT_USER_ROLE = "USER"; 18 | private static final String DEFAULT_ADMIN_ROLE = "ADMIN"; 19 | private static final String DEFAULT_USER_SECRET = "usrSecret"; 20 | private static final String DEFAULT_USER_ID = "user"; 21 | 22 | @Override 23 | protected void configure(AuthenticationManagerBuilder authMgrBldr) throws Exception { 24 | authMgrBldr.inMemoryAuthentication() 25 | .passwordEncoder(org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()) 26 | .withUser(DEFAULT_USER_ID).password(DEFAULT_USER_SECRET).authorities(DEFAULT_USER_ROLE).and() 27 | .withUser(DEFAULT_ADMIN_ID).password(DEFAULT_ADMIN_SECRET) 28 | .authorities(DEFAULT_USER_ROLE, DEFAULT_ADMIN_ROLE); 29 | } 30 | 31 | // for any URL applying the security (basic authentication) 32 | @Override 33 | protected void configure(HttpSecurity httpSec) throws Exception { 34 | // force for credentials every time (stateless) 35 | httpSec.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 36 | // Invalid CSRF Token 'null' was found on the request parameter '_csrf' 37 | // or header 'X-CSRF-TOKEN' 38 | httpSec.csrf().disable(); 39 | 40 | // welcome page no authentication required 41 | // /investors/admin only admin should be able to access 42 | // other pages Users should also be able to access 43 | httpSec.csrf().disable().authorizeRequests().and().authorizeRequests().antMatchers(NO_RESTRICT_WELCOME_URI) 44 | .permitAll().antMatchers("/investors/admin").hasAuthority(DEFAULT_ADMIN_ROLE) 45 | .antMatchers("/investors/invr*/**").access("hasAuthority('" + DEFAULT_USER_ROLE + "')").anyRequest() 46 | .authenticated().and().httpBasic().and().logout(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/InvestorServicesFetchOperations.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.stereotype.Component; 6 | 7 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 8 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 9 | 10 | @Component 11 | public class InvestorServicesFetchOperations { 12 | 13 | public List fetchAllInvestors() { 14 | return InvestorService.investorsList; 15 | } 16 | 17 | public Investor fetchInvestorById(String investorId) { 18 | return InvestorService.investorsList.stream() 19 | .filter(investors -> investorId.equalsIgnoreCase(investors.getId())).findAny().orElse(null); 20 | 21 | } 22 | 23 | public List fetchStocksByInvestorId(String investorId, int offset, int limit) { 24 | Investor investor = fetchInvestorById(investorId); 25 | return investor.getStocks().subList(getStartFrom(offset, investor), getToIndex(offset, limit, investor)); 26 | } 27 | 28 | private int getToIndex(int offset, int limit, Investor investor) { 29 | int toIndex = offset + limit; 30 | return (toIndex) > investor.getStocks().size() ? investor.getStocks().size() : toIndex; 31 | } 32 | 33 | private int getStartFrom(int offset, Investor investor) { 34 | return (offset) >= investor.getStocks().size() ? investor.getStocks().size() : offset; 35 | } 36 | 37 | public Stock fetchSingleStockByInvestorIdAndStockSymbol(String investorId, String symbol) { 38 | Investor investor = fetchInvestorById(investorId); 39 | return investor.getStocks().stream().filter(stock -> symbol.equalsIgnoreCase(stock.getSymbol())).findAny() 40 | .orElse(null); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class InvestorServicesApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.mock.web.MockHttpServletResponse; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.MvcResult; 13 | import org.springframework.test.web.servlet.RequestBuilder; 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 15 | 16 | import com.books.chapters.restfulapi.patterns.chap4.springboot.controller.InvestorController; 17 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 18 | 19 | @RunWith(SpringRunner.class) 20 | @WebMvcTest(value = InvestorController.class, secure = false) 21 | public class InvestorControllerTest { 22 | 23 | @Autowired 24 | private MockMvc mockMvc; 25 | 26 | @MockBean 27 | private InvestorService investorService; 28 | 29 | @Test 30 | public void fetchAllInvestors() throws Exception{ 31 | RequestBuilder requestBuilder = MockMvcRequestBuilders.get( 32 | "/investors").accept( 33 | MediaType.APPLICATION_JSON); 34 | MvcResult result = mockMvc.perform(requestBuilder).andReturn(); 35 | MockHttpServletResponse response = result.getResponse(); 36 | System.out.println("here "+response); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/InvestorServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import org.junit.Test; 4 | 5 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 6 | 7 | import junit.framework.TestCase; 8 | 9 | public class InvestorServiceTest extends TestCase { 10 | 11 | @Override 12 | protected void setUp() throws Exception { 13 | 14 | } 15 | 16 | @Test 17 | public void testAddNewStocksToTheInvestorPortfolio() { 18 | Stock actualStock = new Stock("EXe", 150, 18.5); 19 | getInvestorServiceForTest().addNewStockToTheInvestorPortfolio("invr_1", actualStock); 20 | Stock expectedStock = getInvestorServiceFetchOperationsForTest() 21 | .fetchSingleStockByInvestorIdAndStockSymbol("invr_1", "EXe"); 22 | assertEquals(expectedStock.getSymbol(), actualStock.getSymbol()); 23 | } 24 | 25 | @Test 26 | public void testAddNewStocksToTheInvestorPortfolioFailsWhenTryInsertingDuplicate() { 27 | String investorId = "invr_1"; 28 | String actualStockSymbol = "EXe"; 29 | Stock actualStock = new Stock(actualStockSymbol, 150, 18.5); 30 | InvestorService localInvestorService = getInvestorServiceForTest(); 31 | assertNotNull(localInvestorService.addNewStockToTheInvestorPortfolio(investorId, actualStock)); 32 | assertNull(localInvestorService.addNewStockToTheInvestorPortfolio(investorId, actualStock)); 33 | } 34 | 35 | @Test 36 | public void testDeleteAStockToTheInvestorPortfolio() { 37 | Stock actualStock = new Stock("EXA", 150, 18.5); 38 | getInvestorServiceForTest().deleteStockFromTheInvestorPortfolio("invr_1", actualStock.getSymbol()); 39 | Stock expectedStock = getInvestorServiceFetchOperationsForTest() 40 | .fetchSingleStockByInvestorIdAndStockSymbol("invr_1", "EXa"); 41 | assertNull(expectedStock); 42 | } 43 | 44 | @Test 45 | public void testUpdateAStockByInvestorId() { 46 | Stock expectedStock = new Stock("EXA", 150, 18.5); 47 | InvestorService localInvestorService = getInvestorServiceForTest(); 48 | String investorId = "invr_1"; 49 | Stock actualStock = localInvestorService.updateAStockByInvestorIdAndStock(investorId, expectedStock); 50 | assertEquals(expectedStock.getNumberOfSharesHeld(), actualStock.getNumberOfSharesHeld()); 51 | assertEquals(expectedStock.getTickerPrice(), actualStock.getTickerPrice()); 52 | } 53 | 54 | @Test 55 | public void testUpdateAStockByInvestorIdWhenInvestorIdNotFound() { 56 | Stock expectedStock = new Stock("EXA", 150, 18.5); 57 | InvestorService localInvestorService = getInvestorServiceForTest(); 58 | String investorId = "invr_not_found"; 59 | Stock actualStock = localInvestorService.updateAStockByInvestorIdAndStock(investorId, expectedStock); 60 | assertNull(actualStock); 61 | } 62 | 63 | @Test 64 | public void testUpdateAStockByInvestorIdWhenStockSymbolNotFound() { 65 | Stock expectedStock = new Stock("not_found", 150, 18.5); 66 | InvestorService localInvestorService = getInvestorServiceForTest(); 67 | String investorId = "invr_1"; 68 | Stock actualStock = localInvestorService.updateAStockByInvestorIdAndStock(investorId, expectedStock); 69 | assertNull(actualStock); 70 | } 71 | 72 | private InvestorService getInvestorServiceForTest() { 73 | return new InvestorService(); 74 | } 75 | 76 | private InvestorServicesFetchOperations getInvestorServiceFetchOperationsForTest() { 77 | return new InvestorServicesFetchOperations(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Chapter04/investor-services-bulk/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/InvestorServicesFetchOperationsTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import org.junit.Test; 9 | 10 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 11 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 12 | 13 | public class InvestorServicesFetchOperationsTest { 14 | 15 | @Test 16 | public void testFetchAllInvestors() { 17 | int expectedSize = 2; 18 | int actualListSize = new InvestorServicesFetchOperations().fetchAllInvestors().size(); 19 | assertEquals(expectedSize, actualListSize); 20 | } 21 | 22 | @Test 23 | public void testFetchInvestorById() { 24 | Investor actualInvestor = getInvestorServiceForTest().fetchInvestorById("invr_1"); 25 | Investor expectedInvestor = new Investor("INVR_1", "Investor ONE", "conservative investor", getStocksSetOne()); 26 | assertEquals(actualInvestor.getId(), expectedInvestor.getId()); 27 | actualInvestor = getInvestorServiceForTest().fetchInvestorById("invr_2"); 28 | expectedInvestor = new Investor("INVR_2", "Investor TWO", "Moderate Risk investor", getStocksSetTwo()); 29 | assertEquals(expectedInvestor.getId(), actualInvestor.getId()); 30 | 31 | } 32 | 33 | private InvestorServicesFetchOperations getInvestorServiceForTest() { 34 | return new InvestorServicesFetchOperations(); 35 | } 36 | 37 | @Test 38 | public void testFetchStocksByInvestorId() { 39 | Investor expectedInvestor = getInvestorServiceForTest().fetchInvestorById("invr_1"); 40 | String expectedStockSymbol = "EXB"; 41 | assertEquals(4,expectedInvestor.getStocks().size()); 42 | Stock expectedStock = expectedInvestor.getStocks().stream() 43 | .filter(stock -> expectedStockSymbol.equalsIgnoreCase(stock.getSymbol())).findAny().orElse(null); 44 | assertNotNull(expectedStock); 45 | 46 | } 47 | 48 | @Test 49 | public void testFetchSingleStockByInvestorIdAndStockSymbol() { 50 | String investorId = "Invr_1"; 51 | String expectedSymbol = "EXA"; 52 | String actualSymbol = (getInvestorServiceForTest() 53 | .fetchSingleStockByInvestorIdAndStockSymbol(investorId, expectedSymbol).getSymbol()); 54 | assertEquals(expectedSymbol, actualSymbol); 55 | } 56 | 57 | private List getStocksSetOne() { 58 | ArrayList stocksLotOne = new ArrayList<>(); 59 | Stock stocksSampleOne = new Stock("EXA", 200, 20); 60 | Stock stocksSampleTwo = new Stock("EXB", 100, 60); 61 | 62 | stocksLotOne.add(stocksSampleTwo); 63 | stocksLotOne.add(stocksSampleOne); 64 | return stocksLotOne; 65 | } 66 | 67 | private List getStocksSetTwo() { 68 | ArrayList stocksLotTwo = new ArrayList<>(); 69 | Stock stocksSampleThree = new Stock("EXC", 300, 200); 70 | Stock stocksSampleFour = new Stock("EXD", 150, 40); 71 | 72 | stocksLotTwo.add(stocksSampleThree); 73 | stocksLotTwo.add(stocksSampleFour); 74 | return stocksLotTwo; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.books.chapters.restfulapi.patterns.partOne 7 | investor-services 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | investor-services-facade 12 | sample spring boot application for the the restful API patterns chapter part two 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.4.4.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-security 39 | 40 | 41 | com.fasterxml.jackson.dataformat 42 | jackson-dataformat-xml 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InvestorServicesApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(InvestorServicesApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorServiceFetchController.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestParam; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 14 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 15 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions.InvestorNotFoundException; 16 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorServicesFetchOperations; 17 | 18 | @RestController 19 | @ComponentScan(basePackageClasses = { InvestorServicesFetchOperations.class }) 20 | public class InvestorServiceFetchController { 21 | @Autowired 22 | private InvestorServicesFetchOperations investorServiceFetchOpertions; 23 | 24 | @GetMapping(value = "/welcome", produces = "text/plain;charset=UTF-8") 25 | public String welcomePageWhichProducesCharset() { 26 | return "ウェルカムインベスター (\"Welcome Investor!\" in Japanese)"; 27 | } 28 | 29 | @GetMapping("/investors") 30 | public String welcomeUsers() { 31 | return "You are in right API, However please refer the API docs for specific URLs that can be accessed with your Roles"; 32 | } 33 | 34 | @GetMapping("/investors/admin") 35 | public List fetchAllInvestors() { 36 | return investorServiceFetchOpertions.fetchAllInvestors(); 37 | } 38 | 39 | @GetMapping("/investors/{investorId}") 40 | public Investor fetchInvestorById(@PathVariable String investorId) { 41 | Investor resultantInvestor = investorServiceFetchOpertions.fetchInvestorById(investorId); 42 | if (resultantInvestor == null) { 43 | throw new InvestorNotFoundException("Investor Id-" + investorId); 44 | } 45 | return resultantInvestor; 46 | } 47 | 48 | @GetMapping(path = "/investors/{investorId}/stocks") 49 | public List fetchStocksByInvestorId(@PathVariable String investorId, 50 | @RequestParam(value = "offset", defaultValue = "0") int offset, 51 | @RequestParam(value = "limit", defaultValue = "5") int limit) { 52 | return investorServiceFetchOpertions.fetchStocksByInvestorId(investorId, offset, limit); 53 | } 54 | 55 | /** 56 | * method example which produces both xml and json output, other methods 57 | * produces only json response and for other content type it errors out 406 58 | * content not allowed 59 | */ 60 | @GetMapping(path = "/investors/{investorId}/stocks/{symbol}", produces = { MediaType.APPLICATION_JSON_VALUE, 61 | MediaType.APPLICATION_XML_VALUE }) 62 | public Stock fetchAStockByInvestorIdAndStockId(@PathVariable String investorId, @PathVariable String symbol) { 63 | return investorServiceFetchOpertions.fetchSingleStockByInvestorIdAndStockSymbol(investorId, symbol); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/IndividualInvestorPortfolio.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class IndividualInvestorPortfolio { 4 | private String investorId; 5 | private int stocksHoldCount; 6 | 7 | public IndividualInvestorPortfolio(String investorId, int stocksHoldCount){ 8 | this.investorId = investorId; 9 | this.stocksHoldCount = stocksHoldCount; 10 | } 11 | 12 | public String getInvestorId() { 13 | return investorId; 14 | } 15 | public void setInvestorId(String investorId) { 16 | this.investorId = investorId; 17 | } 18 | public int getStocksHoldCount() { 19 | return stocksHoldCount; 20 | } 21 | public void setStocksHoldCount(int stocksCount) { 22 | stocksHoldCount = stocksCount; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Investor.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | import java.util.List; 4 | 5 | public class Investor { 6 | private String id; 7 | private String name; 8 | private String description; 9 | private List stocks; 10 | 11 | public Investor(String id, String name, String description, List stocks) { 12 | this.id = id; 13 | this.name = name; 14 | this.description = description; 15 | this.stocks = stocks; 16 | } 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | 42 | public List getStocks() { 43 | return stocks; 44 | } 45 | 46 | public void setStocks(List stocks) { 47 | this.stocks = stocks; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return String.format("Investor [id=%s, name=%s, description=%s, stocks=%s]", id, name, description, stocks); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Stock.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class Stock { 4 | private String symbol; 5 | private int numberOfSharesHeld; 6 | private double tickerPrice = 0.0; 7 | 8 | Stock() { 9 | // default constructor, otherwise the spring boot runtime complains that 10 | // it can not instantiate this Stock class when we try to do POST to 11 | // insert a new Stock from the controller classes 12 | } 13 | 14 | public Stock(String symbol, int numberOfSharesHeld, double tickerPrice) { 15 | 16 | this.symbol = symbol; 17 | this.numberOfSharesHeld = numberOfSharesHeld; 18 | this.tickerPrice = tickerPrice; 19 | } 20 | 21 | public String getSymbol() { 22 | return symbol; 23 | } 24 | 25 | public void setSymbol(String symbol) { 26 | this.symbol = symbol; 27 | } 28 | 29 | public int getNumberOfSharesHeld() { 30 | return numberOfSharesHeld; 31 | } 32 | 33 | public void setNumberOfSharesHeld(int numberOfShares) { 34 | this.numberOfSharesHeld = numberOfShares; 35 | } 36 | 37 | public double getTickerPrice() { 38 | return tickerPrice; 39 | } 40 | 41 | public void setTickerPrice(double tickerPrice) { 42 | this.tickerPrice = tickerPrice; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | String pattern = "Stock [symbol: %s, numberOfSharesHeld: %d, tickerPrice: %6.2f]"; 48 | return String.format(pattern, symbol, numberOfSharesHeld, tickerPrice); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/CustomHeaderNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class CustomHeaderNotFoundException extends Exception { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public CustomHeaderNotFoundException(String exception) { 7 | super(exception); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/InvestorNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class InvestorNotFoundException extends RuntimeException{ 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public InvestorNotFoundException(String exception) 8 | { 9 | super(exception); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/VersionNotSupportedException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class VersionNotSupportedException extends Exception { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public VersionNotSupportedException(String exception) 7 | { 8 | super(exception); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/security/PatronsAuthConfig.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.security; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 8 | import org.springframework.security.config.http.SessionCreationPolicy; 9 | 10 | @Configuration 11 | @EnableWebSecurity 12 | public class PatronsAuthConfig extends WebSecurityConfigurerAdapter { 13 | 14 | private static final String NO_RESTRICT_WELCOME_URI = "/welcome"; 15 | private static final String DEFAULT_ADMIN_SECRET = "admSecret"; 16 | private static final String DEFAULT_ADMIN_ID = "admin"; 17 | private static final String DEFAULT_USER_ROLE = "USER"; 18 | private static final String DEFAULT_ADMIN_ROLE = "ADMIN"; 19 | private static final String DEFAULT_USER_SECRET = "usrSecret"; 20 | private static final String DEFAULT_USER_ID = "user"; 21 | 22 | @Override 23 | protected void configure(AuthenticationManagerBuilder authMgrBldr) throws Exception { 24 | authMgrBldr.inMemoryAuthentication() 25 | .passwordEncoder(org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance()) 26 | .withUser(DEFAULT_USER_ID).password(DEFAULT_USER_SECRET).authorities(DEFAULT_USER_ROLE).and() 27 | .withUser(DEFAULT_ADMIN_ID).password(DEFAULT_ADMIN_SECRET) 28 | .authorities(DEFAULT_USER_ROLE, DEFAULT_ADMIN_ROLE); 29 | } 30 | 31 | // for any URL applying the security (basic authentication) 32 | @Override 33 | protected void configure(HttpSecurity httpSec) throws Exception { 34 | // force for credentials every time (stateless) 35 | httpSec.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 36 | // Invalid CSRF Token 'null' was found on the request parameter '_csrf' 37 | // or header 'X-CSRF-TOKEN' 38 | httpSec.csrf().disable(); 39 | 40 | // welcome page no authentication required 41 | // /investors/admin only admin should be able to access 42 | // other pages Users should also be able to access 43 | httpSec 44 | .csrf().disable() 45 | .authorizeRequests() 46 | .and() 47 | .authorizeRequests() 48 | .antMatchers(NO_RESTRICT_WELCOME_URI).permitAll() 49 | .antMatchers("/investors/admin").hasAuthority(DEFAULT_ADMIN_ROLE) 50 | .antMatchers("/investors/invr*/**").access("hasAuthority('"+DEFAULT_USER_ROLE+"')") 51 | .anyRequest().authenticated() 52 | .and() 53 | .httpBasic() 54 | .and() 55 | .logout(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/DeleteServiceFacade.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import java.util.List; 4 | 5 | public interface DeleteServiceFacade { 6 | boolean deleteAStock(String investorId, String stockTobeDeletedSymbol); 7 | boolean deleteStocksInBulk(String investorId, List stocksSymbolsList); 8 | } 9 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/DeleteServiceFacadeImpl.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.stereotype.Component; 8 | 9 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.IndividualInvestorPortfolio; 10 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 11 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 12 | 13 | @Component 14 | public class DeleteServiceFacadeImpl implements DeleteServiceFacade { 15 | 16 | private static final Logger logger = LoggerFactory.getLogger(InvestorService.class); 17 | private InvestorServicesFetchOperations investorServicesFetchOperations = new InvestorServicesFetchOperations(); 18 | 19 | @Override 20 | public boolean deleteAStock(String investorId, String stockTobeDeletedSymbol) { 21 | boolean deletedStatus = false; 22 | 23 | Stock stockTobeDeleted = investorServicesFetchOperations.fetchSingleStockByInvestorIdAndStockSymbol(investorId, 24 | stockTobeDeletedSymbol); 25 | if (stockTobeDeleted != null) { 26 | Investor investor = investorServicesFetchOperations.fetchInvestorById(investorId); 27 | deletedStatus = investor.getStocks().remove(stockTobeDeleted); 28 | } 29 | designForIntentCascadePortfolioDelete(investorId, deletedStatus); 30 | return deletedStatus; 31 | } 32 | 33 | @Override 34 | public boolean deleteStocksInBulk(String investorId, List stocksSymbolsList) { 35 | return false; 36 | } 37 | 38 | private void designForIntentCascadePortfolioDelete(String investorId, boolean deletedStatus) { 39 | IndividualInvestorPortfolio individualInvestorPortfolio = InvestorService.portfoliosMap 40 | .get(investorServicesFetchOperations.fetchInvestorById(investorId).getId()); 41 | if (deletedStatus) { 42 | individualInvestorPortfolio.setStocksHoldCount(individualInvestorPortfolio.getStocksHoldCount() - 1); 43 | logger.info("updated the portfolio for Delete stocks operation"); 44 | } else { 45 | logger.warn("Update to the individual portofolio not pursued as deleted status of DEL operation returned "); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/InvestorServicesFetchOperations.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.stereotype.Component; 6 | 7 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 8 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 9 | 10 | @Component 11 | public class InvestorServicesFetchOperations { 12 | 13 | public List fetchAllInvestors() { 14 | return InvestorService.investorsList; 15 | } 16 | 17 | public Investor fetchInvestorById(String investorId) { 18 | return InvestorService.investorsList.stream() 19 | .filter(investors -> investorId.equalsIgnoreCase(investors.getId())).findAny().orElse(null); 20 | 21 | } 22 | 23 | public List fetchStocksByInvestorId(String investorId, int offset, int limit) { 24 | Investor investor = fetchInvestorById(investorId); 25 | return investor.getStocks().subList(getStartFrom(offset, investor), getToIndex(offset, limit, investor)); 26 | } 27 | 28 | private int getToIndex(int offset, int limit, Investor investor) { 29 | int toIndex = offset + limit; 30 | return (toIndex) > investor.getStocks().size() ? investor.getStocks().size() : toIndex; 31 | } 32 | 33 | private int getStartFrom(int offset, Investor investor) { 34 | return (offset) >= investor.getStocks().size() ? investor.getStocks().size() : offset; 35 | } 36 | 37 | public Stock fetchSingleStockByInvestorIdAndStockSymbol(String investorId, String symbol) { 38 | Investor investor = fetchInvestorById(investorId); 39 | return investor.getStocks().stream().filter(stock -> symbol.equalsIgnoreCase(stock.getSymbol())).findAny() 40 | .orElse(null); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class InvestorServicesApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.mock.web.MockHttpServletResponse; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.MvcResult; 13 | import org.springframework.test.web.servlet.RequestBuilder; 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 15 | 16 | import com.books.chapters.restfulapi.patterns.chap4.springboot.controller.InvestorController; 17 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 18 | 19 | @RunWith(SpringRunner.class) 20 | @WebMvcTest(value = InvestorController.class, secure = false) 21 | public class InvestorControllerTest { 22 | 23 | @Autowired 24 | private MockMvc mockMvc; 25 | 26 | @MockBean 27 | private InvestorService investorServices; 28 | 29 | @Test 30 | public void fetchAllInvestors() throws Exception{ 31 | RequestBuilder requestBuilder = MockMvcRequestBuilders.get( 32 | "/investors").accept( 33 | MediaType.APPLICATION_JSON); 34 | MvcResult result = mockMvc.perform(requestBuilder).andReturn(); 35 | MockHttpServletResponse response = result.getResponse(); 36 | System.out.println("here "+response); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/InvestorServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import org.junit.Test; 4 | 5 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 6 | 7 | import junit.framework.TestCase; 8 | 9 | public class InvestorServiceTest extends TestCase { 10 | 11 | @Override 12 | protected void setUp() throws Exception { 13 | 14 | } 15 | 16 | @Test 17 | public void testAddNewStocksToTheInvestorPortfolio() { 18 | Stock actualStock = new Stock("EXe", 150, 18.5); 19 | getInvestorServiceForTest().addNewStockToTheInvestorPortfolio("invr_1", actualStock); 20 | Stock expectedStock = getInvestorServiceFetchOperationsForTest() 21 | .fetchSingleStockByInvestorIdAndStockSymbol("invr_1", "EXe"); 22 | assertEquals(expectedStock.getSymbol(), actualStock.getSymbol()); 23 | } 24 | 25 | @Test 26 | public void testAddNewStocksToTheInvestorPortfolioFailsWhenTryInsertingDuplicate() { 27 | String investorId = "invr_1"; 28 | String actualStockSymbol = "EXe"; 29 | Stock actualStock = new Stock(actualStockSymbol, 150, 18.5); 30 | InvestorService localInvestorService = getInvestorServiceForTest(); 31 | assertNotNull(localInvestorService.addNewStockToTheInvestorPortfolio(investorId, actualStock)); 32 | assertNull(localInvestorService.addNewStockToTheInvestorPortfolio(investorId, actualStock)); 33 | } 34 | 35 | @Test 36 | public void testDeleteAStockToTheInvestorPortfolio() { 37 | Stock actualStock = new Stock("EXA", 150, 18.5); 38 | getInvestorServiceForTest().deleteAStockFromTheInvestorPortfolio("invr_1", actualStock.getSymbol()); 39 | Stock expectedStock = getInvestorServiceFetchOperationsForTest() 40 | .fetchSingleStockByInvestorIdAndStockSymbol("invr_1", "EXa"); 41 | assertNull(expectedStock); 42 | } 43 | 44 | @Test 45 | public void testUpdateAStockByInvestorId() { 46 | Stock expectedStock = new Stock("EXA", 150, 18.5); 47 | InvestorService localInvestorService = getInvestorServiceForTest(); 48 | String investorId = "invr_1"; 49 | Stock actualStock = localInvestorService.updateAStockByInvestorIdAndStock(investorId, expectedStock); 50 | assertEquals(expectedStock.getNumberOfSharesHeld(), actualStock.getNumberOfSharesHeld()); 51 | assertEquals(expectedStock.getTickerPrice(), actualStock.getTickerPrice()); 52 | } 53 | 54 | @Test 55 | public void testUpdateAStockByInvestorIdWhenInvestorIdNotFound() { 56 | Stock expectedStock = new Stock("EXA", 150, 18.5); 57 | InvestorService localInvestorService = getInvestorServiceForTest(); 58 | String investorId = "invr_not_found"; 59 | Stock actualStock = localInvestorService.updateAStockByInvestorIdAndStock(investorId, expectedStock); 60 | assertNull(actualStock); 61 | } 62 | 63 | @Test 64 | public void testUpdateAStockByInvestorIdWhenStockSymbolNotFound() { 65 | Stock expectedStock = new Stock("not_found", 150, 18.5); 66 | InvestorService localInvestorService = getInvestorServiceForTest(); 67 | String investorId = "invr_1"; 68 | Stock actualStock = localInvestorService.updateAStockByInvestorIdAndStock(investorId, expectedStock); 69 | assertNull(actualStock); 70 | } 71 | 72 | private InvestorService getInvestorServiceForTest() { 73 | return new InvestorService(); 74 | } 75 | 76 | private InvestorServicesFetchOperations getInvestorServiceFetchOperationsForTest() { 77 | return new InvestorServicesFetchOperations(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Chapter04/investor-services-facade/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/service/InvestorServicesFetchOperationsTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.service; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import org.junit.Test; 9 | 10 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Investor; 11 | import com.books.chapters.restfulapi.patterns.chap4.springboot.models.Stock; 12 | 13 | public class InvestorServicesFetchOperationsTest { 14 | 15 | @Test 16 | public void testFetchAllInvestors() { 17 | int expectedSize = 2; 18 | int actualListSize = new InvestorServicesFetchOperations().fetchAllInvestors().size(); 19 | assertEquals(expectedSize, actualListSize); 20 | } 21 | 22 | @Test 23 | public void testFetchInvestorById() { 24 | Investor actualInvestor = getInvestorServiceForTest().fetchInvestorById("invr_1"); 25 | Investor expectedInvestor = new Investor("INVR_1", "Investor ONE", "conservative investor", getStocksSetOne()); 26 | assertEquals(actualInvestor.getId(), expectedInvestor.getId()); 27 | actualInvestor = getInvestorServiceForTest().fetchInvestorById("invr_2"); 28 | expectedInvestor = new Investor("INVR_2", "Investor TWO", "Moderate Risk investor", getStocksSetTwo()); 29 | assertEquals(expectedInvestor.getId(), actualInvestor.getId()); 30 | 31 | } 32 | 33 | private InvestorServicesFetchOperations getInvestorServiceForTest() { 34 | return new InvestorServicesFetchOperations(); 35 | } 36 | 37 | @Test 38 | public void testFetchStocksByInvestorId() { 39 | Investor expectedInvestor = getInvestorServiceForTest().fetchInvestorById("invr_1"); 40 | String expectedStockSymbol = "EXB"; 41 | assertEquals(4,expectedInvestor.getStocks().size()); 42 | Stock expectedStock = expectedInvestor.getStocks().stream() 43 | .filter(stock -> expectedStockSymbol.equalsIgnoreCase(stock.getSymbol())).findAny().orElse(null); 44 | assertNotNull(expectedStock); 45 | 46 | } 47 | 48 | @Test 49 | public void testFetchSingleStockByInvestorIdAndStockSymbol() { 50 | String investorId = "Invr_1"; 51 | String expectedSymbol = "EXA"; 52 | String actualSymbol = (getInvestorServiceForTest() 53 | .fetchSingleStockByInvestorIdAndStockSymbol(investorId, expectedSymbol).getSymbol()); 54 | assertEquals(expectedSymbol, actualSymbol); 55 | } 56 | 57 | private List getStocksSetOne() { 58 | ArrayList stocksLotOne = new ArrayList<>(); 59 | Stock stocksSampleOne = new Stock("EXA", 200, 20); 60 | Stock stocksSampleTwo = new Stock("EXB", 100, 60); 61 | 62 | stocksLotOne.add(stocksSampleTwo); 63 | stocksLotOne.add(stocksSampleOne); 64 | return stocksLotOne; 65 | } 66 | 67 | private List getStocksSetTwo() { 68 | ArrayList stocksLotTwo = new ArrayList<>(); 69 | Stock stocksSampleThree = new Stock("EXC", 300, 200); 70 | Stock stocksSampleFour = new Stock("EXD", 150, 40); 71 | 72 | stocksLotTwo.add(stocksSampleThree); 73 | stocksLotTwo.add(stocksSampleFour); 74 | return stocksLotTwo; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.books.chapters.restfulapi.patterns.partOne 7 | investor-services 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | investor-services-v2 12 | sample spring boot application for the the restful API patterns chapter part two 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.4.4.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | com.fasterxml.jackson.dataformat 38 | jackson-dataformat-xml 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-maven-plugin 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/scripts/buildMyExamples.bat: -------------------------------------------------------------------------------- 1 | mkdir bookexamples 2 | cd bookexamples 3 | mkdir ch03 4 | cd ch03 5 | git clone https://github.com/PacktPublishing/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices.git 6 | cd Hands-On-RESTful-API-Design-Patterns-and-Best-Practices 7 | cd investor-services 8 | mvn clean package -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/scripts/runMyExamples.bat: -------------------------------------------------------------------------------- 1 | java -jar bookexamples/ch03/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices/investor-services/target/investor-services-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InvestorServicesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(InvestorServicesApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/IndividualInvestorPortfolio.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class IndividualInvestorPortfolio { 4 | private String investorId; 5 | private int stocksHoldCount; 6 | 7 | public IndividualInvestorPortfolio(String investorId, int stocksHoldCount){ 8 | this.investorId = investorId; 9 | this.stocksHoldCount = stocksHoldCount; 10 | } 11 | 12 | public String getInvestorId() { 13 | return investorId; 14 | } 15 | public void setInvestorId(String investorId) { 16 | this.investorId = investorId; 17 | } 18 | public int getStocksHoldCount() { 19 | return stocksHoldCount; 20 | } 21 | public void setStocksHoldCount(int stocksCount) { 22 | stocksHoldCount = stocksCount; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Investor.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | import java.util.List; 4 | 5 | public class Investor { 6 | private String id; 7 | private String name; 8 | private String description; 9 | private List stocks; 10 | 11 | public Investor(String id, String name, String description, List stocks) { 12 | this.id = id; 13 | this.name = name; 14 | this.description = description; 15 | this.stocks = stocks; 16 | } 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | 42 | public List getStocks() { 43 | return stocks; 44 | } 45 | 46 | public void setStocks(List stocks) { 47 | this.stocks = stocks; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return String.format("Investor [id=%s, name=%s, description=%s, stocks=%s]", id, name, description, stocks); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/Stock.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models; 2 | 3 | public class Stock { 4 | private String symbol; 5 | private int numberOfSharesHeld; 6 | private double tickerPrice = 0.0; 7 | 8 | Stock() { 9 | // default constructor, otherwise the spring boot runtime complains that 10 | // it can not instantiate this Stock class when we try to do POST to 11 | // insert a new Stock from the controller classes 12 | } 13 | 14 | public Stock(String symbol, int numberOfSharesHeld, double tickerPrice) { 15 | 16 | this.symbol = symbol; 17 | this.numberOfSharesHeld = numberOfSharesHeld; 18 | this.tickerPrice = tickerPrice; 19 | } 20 | 21 | public String getSymbol() { 22 | return symbol; 23 | } 24 | 25 | public void setSymbol(String symbol) { 26 | this.symbol = symbol; 27 | } 28 | 29 | public int getNumberOfSharesHeld() { 30 | return numberOfSharesHeld; 31 | } 32 | 33 | public void setNumberOfSharesHeld(int numberOfShares) { 34 | this.numberOfSharesHeld = numberOfShares; 35 | } 36 | 37 | public double getTickerPrice() { 38 | return tickerPrice; 39 | } 40 | 41 | public void setTickerPrice(double tickerPrice) { 42 | this.tickerPrice = tickerPrice; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | String pattern = "Stock [symbol: %s, numberOfSharesHeld: %d, tickerPrice: %6.2f]"; 48 | return String.format(pattern, symbol, numberOfSharesHeld, tickerPrice); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/InvestorNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class InvestorNotFoundException extends RuntimeException{ 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public InvestorNotFoundException(String exception) 8 | { 9 | super(exception); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/src/main/java/com/books/chapters/restfulapi/patterns/chap4/springboot/models/errorsandexceptions/VersionNotSupportedException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.models.errorsandexceptions; 2 | 3 | public class VersionNotSupportedException extends Exception { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public VersionNotSupportedException(String exception) 7 | { 8 | super(exception); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=9090 -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/InvestorServicesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class InvestorServicesApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/investor-services-v2/src/test/java/com/books/chapters/restfulapi/patterns/chap4/springboot/controller/InvestorControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap4.springboot.controller; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.mock.web.MockHttpServletResponse; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.MvcResult; 13 | import org.springframework.test.web.servlet.RequestBuilder; 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 15 | 16 | import com.books.chapters.restfulapi.patterns.chap4.springboot.controller.InvestorController; 17 | import com.books.chapters.restfulapi.patterns.chap4.springboot.service.InvestorService; 18 | 19 | @RunWith(SpringRunner.class) 20 | @WebMvcTest(value = InvestorController.class, secure = false) 21 | public class InvestorControllerTest { 22 | 23 | @Autowired 24 | private MockMvc mockMvc; 25 | 26 | @MockBean 27 | private InvestorService investorService; 28 | 29 | @Test 30 | public void fetchAllInvestors() throws Exception{ 31 | RequestBuilder requestBuilder = MockMvcRequestBuilders.get( 32 | "/investors").accept( 33 | MediaType.APPLICATION_JSON); 34 | MvcResult result = mockMvc.perform(requestBuilder).andReturn(); 35 | MockHttpServletResponse response = result.getResponse(); 36 | System.out.println("here "+response); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.books.chapters.restfulapi.patterns.partTwo 7 | oauth2-sample 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | OAuth2 sample 12 | sample spring boot application for the the restful API patterns 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.5.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | Finchley.SR1 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-security 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-web 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-starter-oauth2 40 | 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-test 46 | test 47 | 48 | 49 | org.springframework.security 50 | spring-security-test 51 | test 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-thymeleaf 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.cloud 63 | spring-cloud-dependencies 64 | ${spring-cloud.version} 65 | pom 66 | import 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/java/com/books/chapters/restfulapi/patterns/advanced/oauth/AuthorizacionServerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.advanced.oauth; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Qualifier; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.authentication.AuthenticationManager; 8 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 9 | import org.springframework.security.crypto.password.PasswordEncoder; 10 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 11 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 12 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 13 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 14 | import org.springframework.security.oauth2.provider.token.TokenStore; 15 | import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; 16 | 17 | @Configuration 18 | @EnableAuthorizationServer 19 | public class AuthorizacionServerConfiguration extends AuthorizationServerConfigurerAdapter { 20 | 21 | @Autowired 22 | @Qualifier("authenticationManagerBean") 23 | private AuthenticationManager authenticationManager; 24 | 25 | @Autowired 26 | private TokenStore tokenStore; 27 | 28 | 29 | @Override 30 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 31 | 32 | clients.inMemory() 33 | .withClient("client") 34 | .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit") 35 | .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT","USER") 36 | .scopes("read","write") 37 | .autoApprove(true) 38 | .secret(passwordEncoder().encode("client-secret1@3")); 39 | 40 | } 41 | 42 | @Bean 43 | public PasswordEncoder passwordEncoder() { 44 | return new BCryptPasswordEncoder(); 45 | } 46 | @Override 47 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 48 | endpoints 49 | .authenticationManager(authenticationManager) 50 | .tokenStore(tokenStore); 51 | } 52 | 53 | @Bean 54 | public TokenStore tokenStore() { 55 | return new InMemoryTokenStore(); 56 | } 57 | // 58 | } 59 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/java/com/books/chapters/restfulapi/patterns/advanced/oauth/ResourceServerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.advanced.oauth; 2 | 3 | 4 | 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 7 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | 12 | @EnableResourceServer 13 | @RestController 14 | public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter 15 | { 16 | @RequestMapping("/try") 17 | public String welcome() { 18 | return "Welcome"; 19 | } 20 | @RequestMapping("/userService") 21 | public String userService() { 22 | return "Users"; 23 | } 24 | @RequestMapping("/admin") 25 | public String admin() { 26 | return "Administrator"; 27 | } 28 | @Override 29 | public void configure(HttpSecurity http) throws Exception { 30 | http 31 | .authorizeRequests().antMatchers("/oauth/token", "/oauth/authorize**", "/try").permitAll(); 32 | http.requestMatchers().antMatchers("/userService") 33 | .and().authorizeRequests() 34 | .antMatchers("/userService").access("hasRole('USER')") 35 | .and().requestMatchers().antMatchers("/admin") 36 | .and().authorizeRequests() 37 | .antMatchers("/admin").access("hasRole('ADMIN')"); 38 | } 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/java/com/books/chapters/restfulapi/patterns/advanced/oauth/WebController.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.advanced.oauth; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | @Controller 7 | public class WebController { 8 | 9 | @RequestMapping({"/","index"}) 10 | public String index() { 11 | return "index"; 12 | } 13 | 14 | @RequestMapping("/users") 15 | public String users() { 16 | return "users"; 17 | } 18 | @RequestMapping("/welcome") 19 | public String welcome() { 20 | return "welcome"; 21 | } 22 | @RequestMapping("/webadmin") 23 | public String admin() { 24 | return "admin"; 25 | } 26 | @RequestMapping("/login") 27 | public String login() { 28 | return "login"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/java/com/books/chapters/restfulapi/patterns/advanced/oauth/WebSecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.advanced.oauth; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.security.authentication.AuthenticationManager; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 10 | import org.springframework.security.core.userdetails.User; 11 | import org.springframework.security.core.userdetails.UserDetails; 12 | import org.springframework.security.core.userdetails.UserDetailsService; 13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 14 | import org.springframework.security.crypto.password.PasswordEncoder; 15 | import org.springframework.security.provisioning.InMemoryUserDetailsManager; 16 | 17 | 18 | @SpringBootApplication 19 | @EnableWebSecurity 20 | public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { 21 | 22 | public static void main(String[] args) { 23 | SpringApplication.run(WebSecurityConfiguration.class, args); 24 | } 25 | 26 | @Bean 27 | @Override 28 | public AuthenticationManager authenticationManagerBean() throws Exception { 29 | return super.authenticationManagerBean(); 30 | } 31 | 32 | @Bean 33 | @Override 34 | public UserDetailsService userDetailsService() { 35 | 36 | UserDetails user1=User.builder().username("user1").password(passwordEncoder().encode("secret!23")). 37 | roles("USER").build(); 38 | UserDetails user2=User.builder().username("user2").password(passwordEncoder().encode("secret1@3")). 39 | roles("USER").build(); 40 | UserDetails adminUser=User.builder().username("admin").password(passwordEncoder().encode("secret@dmin")). 41 | roles("ADMIN").build(); 42 | return new InMemoryUserDetailsManager(user1, user2, adminUser); 43 | } 44 | @Bean 45 | public PasswordEncoder passwordEncoder() { 46 | return new BCryptPasswordEncoder(); 47 | } 48 | 49 | 50 | @Override 51 | protected void configure(HttpSecurity http) throws Exception { 52 | http 53 | .csrf().disable() 54 | .authorizeRequests() 55 | .antMatchers("/","/index","/welcome").permitAll() 56 | .antMatchers("/users").authenticated() 57 | .antMatchers("/webadmin").hasRole("ADMIN").and() 58 | .formLogin() 59 | .loginPage("/login") 60 | .permitAll() 61 | .and() 62 | .logout() 63 | .permitAll(); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices/92544a10bee84b777c98b3fc6cd38a5a8e886066/Chapter06/oauth2-sample/src/main/resources/application.properties -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/resources/application1.yml: -------------------------------------------------------------------------------- 1 | security: 2 | oauth2: 3 | client: 4 | client-id: client 5 | client-secret: client-secret1@3 6 | scope: read,write 7 | auto-approve-scopes: '.*' -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/resources/templates/admin.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | OAuth2 sample 6 | 7 | 8 |

OAuth 2 Sample

9 | you are viewing a administrator page as you are using your administrator credentials 10 | 11 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | OAuth 2 Sample 6 | 7 | 8 |

Home

9 |

Please here to see a public page.

10 |

Noraml user click here to see your allowed pages

11 |

Administrator user click here to go to admin section

12 |
13 | Hello someone 14 |

logout

15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | Spring Security Example 6 | 7 | 8 |
9 | Invalid username and password. 10 |
11 |
12 | You have been logged out. 13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/resources/templates/users.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | OAuth2 sample 6 | 7 | 8 |

user page

9 | you are viewing a your (private) page as you are using your user credentials 10 | 11 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/main/resources/templates/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | OAtuh2 sample 6 | 7 | 8 |

Welcome

9 | you are viewing a welcome page and this page doesen't need any credentials 10 | 11 | -------------------------------------------------------------------------------- /Chapter06/oauth2-sample/src/test/java/com/books/chapters/restfulapi/patterns/advanced/oauth/WebSecurityConfigurationTests.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.advanced.oauth; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class WebSecurityConfigurationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter06/test.txt: -------------------------------------------------------------------------------- 1 | initial commit -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.books.chapters.restfulapi.patterns.parttwo 6 | amqp-adapter-module 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | amqp-adapter-module 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 1.8 18 | 2.0.2.RELEASE 19 | 3.8.1 20 | 2.1 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-parent 25 | 1.4.4.RELEASE 26 | 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-dependencies 33 | ${springBoot.version} 34 | pom 35 | import 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-actuator 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-amqp 49 | 50 | 51 | org.apache.commons 52 | commons-lang3 53 | 54 | 55 | javax.validation 56 | validation-api 57 | 58 | 59 | junit 60 | junit 61 | 62 | 63 | javax.ws.rs 64 | javax.ws.rs-api 65 | ${ws.rs.api.version} 66 | 67 | 68 | 69 | 70 | 71 | org.jsonschema2pojo 72 | jsonschema2pojo-maven-plugin 73 | 0.5.1 74 | 75 | true 76 | ${basedir}/src/main/resources/schemas 77 | com.books.chapters.restfulapi.patterns.parttwo.adapter.entity 78 | ${basedir}/generated-src 79 | true 80 | true 81 | true 82 | true 83 | 84 | 85 | 86 | 87 | generate 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/App.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo; 2 | 3 | /** 4 | * Hello world! 5 | * 6 | */ 7 | public class App 8 | { 9 | public static void main( String[] args ) 10 | { 11 | System.out.println( "Hello World!" ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/adapter/amqp/consumer/AmqpSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.consumer; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | 8 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceRequest; 9 | 10 | public class AmqpSubscriber { 11 | private static final Logger LOG = LoggerFactory.getLogger(AmqpSubscriber.class); 12 | 13 | @Autowired 14 | EventHandler eventHandler; 15 | 16 | @RabbitListener(queues = "#{eventQueue.name}") 17 | public void process(ServiceRequest request) { 18 | LOG.trace("Subscribed event: {}", request); 19 | LOG.trace("Handled by: {}", eventHandler.getClass()); 20 | eventHandler.processEvent(request); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/adapter/amqp/consumer/Configurations.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.consumer; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.stream.Collectors; 7 | import java.util.stream.Stream; 8 | 9 | import org.springframework.amqp.core.Binding; 10 | import org.springframework.amqp.core.BindingBuilder; 11 | import org.springframework.amqp.core.Queue; 12 | import org.springframework.amqp.core.TopicExchange; 13 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 14 | import org.springframework.amqp.support.converter.MessageConverter; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.context.annotation.Bean; 17 | import org.springframework.context.annotation.Configuration; 18 | import org.springframework.context.annotation.Profile; 19 | 20 | 21 | @Profile({ "amqp-consumer" }) 22 | @Configuration 23 | public class Configurations { 24 | private Map exchanegMap = new HashMap<>(); 25 | 26 | @Autowired 27 | RabbitMqConfig rabbitMqConfig; 28 | 29 | @Bean 30 | public MessageConverter jsonMessageConverter() { 31 | return new Jackson2JsonMessageConverter(); 32 | } 33 | 34 | @Bean 35 | public Queue serviceQueue() { 36 | return new Queue(rabbitMqConfig.getServiceQueueName()); 37 | } 38 | 39 | @Bean 40 | public List exchanges() { 41 | return Stream.concat(serviceExchanges().stream(), eventExchanges().stream()).collect(Collectors.toList()); 42 | } 43 | 44 | @Bean 45 | public List bindings() { 46 | return Stream.concat(serviceBindings().stream(), eventBindings().stream()).collect(Collectors.toList()); 47 | } 48 | 49 | @Bean 50 | public RpcServer server() { 51 | return new RpcServer(); 52 | } 53 | 54 | @Bean 55 | public Queue eventQueue() { 56 | return new Queue(rabbitMqConfig.getEventQueueName()); 57 | } 58 | 59 | @Bean 60 | public AmqpSubscriber subscriber() { 61 | return new AmqpSubscriber(); 62 | } 63 | 64 | private List serviceExchanges() { 65 | return rabbitMqConfig.getServiceRoutings().stream().map(rabbitMqConfig::getExchangeName).map(e -> { 66 | TopicExchange exchange = new TopicExchange(e); 67 | exchanegMap.put(e, exchange); 68 | return exchange; 69 | }).collect(Collectors.toList()); 70 | } 71 | 72 | private List eventExchanges() { 73 | return rabbitMqConfig.getEventRoutings().stream().map(rabbitMqConfig::getExchangeName).map(e -> { 74 | TopicExchange exchange = new TopicExchange(e); 75 | exchanegMap.put(e, exchange); 76 | return exchange; 77 | }).collect(Collectors.toList()); 78 | } 79 | 80 | private List serviceBindings() { 81 | return rabbitMqConfig 82 | .getServiceRoutings().stream().map(r -> BindingBuilder.bind(serviceQueue()) 83 | .to(exchanegMap.get(rabbitMqConfig.getExchangeName(r))).with(rabbitMqConfig.getRoutingKey(r))) 84 | .collect(Collectors.toList()); 85 | } 86 | 87 | private List eventBindings() { 88 | return rabbitMqConfig 89 | .getEventRoutings().stream().map(r -> BindingBuilder.bind(eventQueue()) 90 | .to(exchanegMap.get(rabbitMqConfig.getExchangeName(r))).with(rabbitMqConfig.getRoutingKey(r))) 91 | .collect(Collectors.toList()); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/adapter/amqp/consumer/EventHandler.java: -------------------------------------------------------------------------------- 1 | 2 | package com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.consumer; 3 | 4 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceRequest; 5 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 6 | 7 | 8 | public interface EventHandler { 9 | public ServiceResponse processRequest(ServiceRequest request); 10 | 11 | public void processEvent(ServiceRequest request); 12 | } 13 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/adapter/amqp/consumer/RabbitMqConfig.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.consumer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | @ConfigurationProperties(prefix = "spring.rabbitmq") 11 | public class RabbitMqConfig { 12 | private static final String DEFAULT_EVENT_QUEUE = "DefaultEventQueue"; 13 | private static final String DEFAULT_SERVICE_QUEUE = "DefaultServiceQueue"; 14 | private List serviceRoutings = new ArrayList<>(); 15 | private List eventRoutings = new ArrayList<>(); 16 | 17 | public List getServiceRoutings() { 18 | return this.serviceRoutings; 19 | } 20 | 21 | public List getEventRoutings() { 22 | return this.eventRoutings; 23 | } 24 | 25 | public String getServiceQueueName() { 26 | return serviceRoutings.stream().findFirst().map(this::getQueue).orElse(DEFAULT_SERVICE_QUEUE); 27 | } 28 | 29 | public String getEventQueueName() { 30 | return eventRoutings.stream().findFirst().map(this::getQueue).orElse(DEFAULT_EVENT_QUEUE); 31 | } 32 | 33 | public String getExchangeName(String routing) { 34 | return routing.split("->")[0]; 35 | } 36 | 37 | public String getRoutingKey(String routing) { 38 | return routing.split("->")[1]; 39 | } 40 | 41 | public String getQueue(String routing) { 42 | return routing.split("->")[2]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/adapter/amqp/consumer/RpcServer.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.consumer; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | 8 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceRequest; 9 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 10 | 11 | public class RpcServer { 12 | private static final Logger LOG = LoggerFactory.getLogger(RpcServer.class); 13 | 14 | @Autowired 15 | EventHandler eventHandler; 16 | 17 | @RabbitListener(queues = "#{serviceQueue.name}") 18 | public ServiceResponse process(ServiceRequest request) { 19 | LOG.trace("RPC service request: {}", request); 20 | LOG.trace("Handled by: {}", eventHandler.getClass()); 21 | return eventHandler.processRequest(request); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/adapter/amqp/producer/AmqpPublisher.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.amqp.core.TopicExchange; 6 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | 9 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceRequest; 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 11 | 12 | public class AmqpPublisher { 13 | private static final Logger LOG = LoggerFactory.getLogger(AmqpPublisher.class); 14 | 15 | @Autowired 16 | private RabbitTemplate template; 17 | 18 | @Autowired 19 | private TopicExchange pubExchange; 20 | 21 | public void publishEvent(ServiceRequest request) { 22 | LOG.trace("Exchange: {} ", pubExchange); 23 | LOG.trace("Service Request: {}", request); 24 | String routingKey = request.getServiceName() + "." + request.getServiceAction(); 25 | template.convertAndSend(pubExchange.getName(), routingKey, request); 26 | } 27 | 28 | public void publishError(ServiceResponse errorResponse, String routingKey) { 29 | LOG.trace("Exchange: {}", pubExchange); 30 | LOG.trace("Service Error: {}", errorResponse); 31 | template.convertAndSend(pubExchange.getName(), routingKey, errorResponse); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/adapter/amqp/producer/ProducerConfigurations.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer; 2 | 3 | import org.springframework.amqp.core.TopicExchange; 4 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 5 | import org.springframework.amqp.support.converter.MessageConverter; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Profile; 10 | 11 | @Profile({ "amqp-producer" }) 12 | @Configuration 13 | public class ProducerConfigurations { 14 | @Value("${spring.rabbitmq.exchange.rpc}") 15 | private String exchangeRpc; 16 | 17 | @Value("${spring.rabbitmq.exchange.pub}") 18 | public String exchangePub; 19 | 20 | @Bean 21 | public TopicExchange rpcExchange() { 22 | return new TopicExchange(exchangeRpc); 23 | } 24 | 25 | @Bean 26 | public RpcClient rpcClient() { 27 | return new RpcClient(); 28 | } 29 | 30 | @Bean 31 | public TopicExchange pubExchange() { 32 | return new TopicExchange(exchangePub); 33 | } 34 | 35 | @Bean 36 | public AmqpPublisher pubClient() { 37 | return new AmqpPublisher(); 38 | 39 | } 40 | 41 | @Bean 42 | public MessageConverter jsonMessageConverter() { 43 | return new Jackson2JsonMessageConverter(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/adapter/amqp/producer/RpcClient.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer; 2 | 3 | import java.util.Date; 4 | import java.util.Optional; 5 | import java.util.UUID; 6 | 7 | import javax.ws.rs.core.Response; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.amqp.core.TopicExchange; 12 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | 15 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ErrorMessage; 16 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceRequest; 17 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 18 | 19 | public class RpcClient { 20 | private static final Logger LOG = LoggerFactory.getLogger(RpcClient.class); 21 | 22 | @Autowired 23 | private RabbitTemplate template; 24 | 25 | @Autowired 26 | private TopicExchange rpcExchange; 27 | 28 | public ServiceResponse invokeService(ServiceRequest request) { 29 | LOG.trace("Exchange: {} ", rpcExchange); 30 | LOG.trace("Service Request: {}", request); 31 | String routingKey = request.getServiceName() + "." + request.getServiceAction(); 32 | ServiceResponse response = (ServiceResponse) template.convertSendAndReceive(rpcExchange.getName(), routingKey, 33 | request); 34 | LOG.trace("Service Response: {}", response); 35 | return Optional.ofNullable(response).orElse(generateTimedoutResponse(request)); 36 | } 37 | 38 | private ServiceResponse generateTimedoutResponse(ServiceRequest request) { 39 | ServiceResponse response = new ServiceResponse().withId(UUID.randomUUID().toString()).withCreatedBy("System") 40 | .withCreatedDate(new Date()) 41 | .withErrorMessage(new ErrorMessage().withCode("ERR_SERVICE_UNAVAIL").withMessage("Service Unavaialble") 42 | .withDetails("Internal Error: we are sorry, the " + request.getServiceName() 43 | + " is not reachable. Please try again later.")) 44 | .withStatusCode(Response.Status.SERVICE_UNAVAILABLE.toString()).withRelatedRequest(request.getId()); 45 | LOG.trace("Service timeout response: {}", response); 46 | return response; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/resources/schemas/BusinessEntity.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "id": "BusinessEntity.json", 4 | "title": "BusinessEntity", 5 | "description": "Business entity model", 6 | "type": "object", 7 | "javaType": "com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity", 8 | "additionalProperties": false, 9 | "properties": { 10 | "id": { 11 | "type": "string" 12 | }, 13 | "entityType": { 14 | "type": "string" 15 | }, 16 | "entitySpecification": { 17 | "type": "string" 18 | }, 19 | "name": { 20 | "type": "string" 21 | }, 22 | "status": { 23 | "type": "string" 24 | }, 25 | "relatedEntities": { 26 | "type": "array", 27 | "items": { 28 | "$ref": "BusinessEntity.json#" 29 | } 30 | } 31 | }, 32 | "required": [ 33 | "id" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/resources/schemas/ErrorMessage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "id": "ErrorMessage.json", 4 | "title": "ErrorMessage", 5 | "description": "Errors", 6 | "type": "object", 7 | "javaType": "com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ErrorMessage", 8 | "additionalProperties": false, 9 | "properties": { 10 | "code": { 11 | "type": "string", 12 | "description": "error code short desc." 13 | }, 14 | "message": { 15 | "type": "string", 16 | "description": "error message." 17 | }, 18 | "details": { 19 | "type": "string", 20 | "description": "error description." 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/resources/schemas/ServiceRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "id": "ServiceRequest.json", 4 | "title": "Service Request", 5 | "type": "object", 6 | "javaType": "com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceRequest", 7 | "additionalProperties": false, 8 | "properties": { 9 | "id": { 10 | "type": "string" 11 | }, 12 | "serviceName": { 13 | "type": "string" 14 | }, 15 | "serviceAction": { 16 | "type": "string" 17 | }, 18 | "createdDate": { 19 | "type": "string", 20 | "format": "date-time" 21 | }, 22 | "createdBy": { 23 | "type": "string", 24 | "description": "The user or system who created this request" 25 | }, 26 | "items": { 27 | "type": "array", 28 | "items": { 29 | "$ref": "BusinessEntity.json#" 30 | } 31 | } 32 | }, 33 | "required": [ 34 | "id", 35 | "serviceName", 36 | "serviceAction", 37 | "items" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/main/resources/schemas/ServiceResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-06/schema#", 3 | "id": "ServiceResponse.json", 4 | "title": "Service Response", 5 | "type": "object", 6 | "javaType": "com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse", 7 | "additionalProperties": false, 8 | "properties": { 9 | "id": { 10 | "type": "string" 11 | }, 12 | "relatedRequest": { 13 | "type": "string" 14 | }, 15 | "createdDate": { 16 | "type": "string", 17 | "format": "date-time" 18 | }, 19 | "createdBy": { 20 | "type": "string", 21 | "description": "The user or system who created this response" 22 | }, 23 | "statusCode": { 24 | "type": "string", 25 | "description": "The status code indicating service invocation is successful or not" 26 | }, 27 | "errorMessage": { 28 | "$ref": "ErrorMessage.json#" 29 | }, 30 | "items": { 31 | "type": "array", 32 | "items": { 33 | "$ref": "BusinessEntity.json#" 34 | } 35 | } 36 | }, 37 | "required": [ 38 | "id", 39 | "relatedRequest" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/amqp-adapter-module/src/test/java/com/books/chapters/restfulapi/patterns/parttwo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | 7 | com.books.chapters.restfulapi.patterns.parttwo 8 | event-driven-service-orchestration 9 | pom 10 | 1.0-SNAPSHOT 11 | Event driven service orchestration 12 | 13 | 14 | amqp-adapter-module 15 | service-stubs-module 16 | shopping-cart 17 | 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 26 | 1.8 27 | 1.8 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | junit 37 | junit 38 | 3.8.1 39 | test 40 | 41 | 42 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/service-stubs-module/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.books.chapters.restfulapi.patterns.parttwo 6 | service-stubs-modules 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | service-stubs-modules 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 2.0.2.RELEASE 16 | 3.8.1 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-parent 21 | 1.4.4.RELEASE 22 | 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-dependencies 29 | ${springBoot.version} 30 | pom 31 | import 32 | 33 | 34 | 35 | 36 | 37 | junit 38 | junit 39 | test 40 | 41 | 42 | com.books.chapters.restfulapi.patterns.parttwo 43 | amqp-adapter-module 44 | 0.0.1-SNAPSHOT 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/service-stubs-module/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/stub/StubServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.stub; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | @SpringBootApplication 8 | @ComponentScan("com.books.chapters.restfulapi.patterns.parttwo") 9 | public class StubServiceApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(StubServiceApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/service-stubs-module/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: default 3 | rabbitmq: 4 | host: localhost 5 | port: 5777 6 | username: test 7 | password: test 8 | 9 | --- 10 | 11 | spring: 12 | profiles: location 13 | include: 14 | - default 15 | rabbitmq: 16 | serviceRoutings: 17 | - shoppingcart.rpc->LocationService.*->LocationServiceQueue 18 | - customer.rpc->LocationService.*->LocationServiceQueue 19 | eventRoutings: 20 | - shoppingcart.pub->LocationService.*->LocationEventQueue 21 | - customer.pub->LocationService.*->LocationEventQueue 22 | 23 | --- 24 | 25 | spring: 26 | profiles: payment 27 | include: 28 | - default 29 | rabbitmq: 30 | serviceRoutings: 31 | - shoppingcart.rpc->PaymentService.*->PaymentServiceQueue 32 | - customer.rpc->PaymentService.*->PaymentServiceQueue 33 | 34 | --- 35 | 36 | spring: 37 | profiles: inventory 38 | include: 39 | - default 40 | rabbitmq: 41 | serviceRoutings: 42 | - shoppingcart.rpc->InventoryService.*->InventoryServiceQueue 43 | - catalog.rpc->InventoryService.*->InventoryServiceQueue 44 | 45 | --- 46 | 47 | spring: 48 | profiles: order 49 | include: 50 | - default 51 | rabbitmq: 52 | serviceRoutings: 53 | - shoppingcart.rpc->OrderService.*->OrderServiceQueue 54 | - customer.rpc->OrderService.*->OrderServiceQueue 55 | 56 | --- 57 | 58 | spring: 59 | profiles: customer 60 | include: 61 | - default 62 | rabbitmq: 63 | serviceRoutings: 64 | - shoppingcart.rpc->CustomerService.*->CustomerServiceQueue 65 | eventRoutings: 66 | - shoppingcart.pub->CustomerService.*->CustomerEventQueue 67 | 68 | --- 69 | 70 | spring: 71 | profiles: backoffice 72 | include: 73 | - default 74 | rabbitmq: 75 | eventRoutings: 76 | - shoppingcart.pub->BackofficeService.notify->BackofficeEventQueue 77 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/service-stubs-module/src/test/java/com/books/chapters/restfulapi/patterns/parttwo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.books.chapters.restfulapi.patterns.parttwo 6 | shopping-cart 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | shopping-cart 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 2.1.1.RELEASE 16 | 3.8.1 17 | 2.3.0 18 | 3.8.0 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-parent 23 | 1.4.4.RELEASE 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-dependencies 31 | ${springBoot.version} 32 | pom 33 | import 34 | 35 | 36 | 37 | 38 | 39 | 40 | com.books.chapters.restfulapi.patterns.parttwo 41 | amqp-adapter-module 42 | 0.0.1-SNAPSHOT 43 | 44 | 45 | org.camunda.bpm.springboot 46 | camunda-bpm-spring-boot-starter-webapp 47 | ${camundaSpringBoot.version} 48 | 49 | 50 | com.h2database 51 | h2 52 | 53 | 54 | org.camunda.bpm.springboot 55 | camunda-bpm-spring-boot-starter-rest 56 | ${camundaSpringBoot.version} 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-data-jpa 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.apache.maven.plugins 68 | maven-compiler-plugin 69 | ${maven.compiler.plugin.version} 70 | 71 | 1.8 72 | 1.8 73 | 74 | 75 | 76 | org.springframework.boot 77 | spring-boot-maven-plugin 78 | ${springBoot.version} 79 | 80 | 81 | 82 | repackage 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/ShoppingCartServiceApp.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo; 2 | 3 | import org.camunda.bpm.spring.boot.starter.annotation.EnableProcessApplication; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | @EnableProcessApplication 9 | public class ShoppingCartServiceApp { 10 | 11 | public static void main(String... args) { 12 | SpringApplication.run(ShoppingCartServiceApp.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/AllocateInventoryActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer.RpcClient; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 12 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 13 | 14 | 15 | @Component 16 | public class AllocateInventoryActivity implements JavaDelegate { 17 | private static final Logger LOG = LoggerFactory.getLogger(AllocateInventoryActivity.class); 18 | 19 | public static final String SERVICE_ACTION = "allocate"; 20 | 21 | @Autowired 22 | RpcClient rpcClient; 23 | 24 | @Override 25 | public void execute(DelegateExecution execution) throws Exception { 26 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_INVENTORY, SERVICE_ACTION); 27 | BusinessEntity sc = (BusinessEntity) execution.getVariable(ProcessConstants.VAR_SC); 28 | ServiceResponse response = rpcClient.invokeService( 29 | ProcessUtil.buildServiceRequest(sc, ProcessConstants.SERVICE_NAME_INVENTORY, SERVICE_ACTION)); 30 | ProcessUtil.processResponse(execution, response); 31 | execution.setVariable(ProcessConstants.VAR_INVENTORY_ALLOCATED, true); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/CancelOrderActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer.RpcClient; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 12 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 13 | 14 | 15 | @Component 16 | public class CancelOrderActivity implements JavaDelegate { 17 | private static final Logger LOG = LoggerFactory.getLogger(CancelOrderActivity.class); 18 | 19 | public static final String SERVICE_ACTION = "cancel"; 20 | 21 | @Autowired 22 | RpcClient rpcClient; 23 | 24 | @Override 25 | public void execute(DelegateExecution execution) throws Exception { 26 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_ORDER, SERVICE_ACTION); 27 | BusinessEntity sc = (BusinessEntity) execution.getVariable(ProcessConstants.VAR_SC); 28 | ServiceResponse response = rpcClient.invokeService( 29 | ProcessUtil.buildServiceRequest(sc, ProcessConstants.SERVICE_NAME_ORDER, SERVICE_ACTION)); 30 | ProcessUtil.processResponse(execution, response); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/CloseShoppingCartActivity.java: -------------------------------------------------------------------------------- 1 | 2 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 3 | 4 | import org.camunda.bpm.engine.delegate.DelegateExecution; 5 | import org.camunda.bpm.engine.delegate.JavaDelegate; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | 11 | import com.books.chapters.restfulapi.patterns.parttwo.dataaccess.ShoppingCartManager; 12 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 13 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 14 | 15 | 16 | @Component 17 | public class CloseShoppingCartActivity implements JavaDelegate { 18 | private static final Logger LOG = LoggerFactory.getLogger(CloseShoppingCartActivity.class); 19 | public static final String SERVICE_ACTION = "close"; 20 | 21 | @Autowired 22 | ShoppingCartManager shoppingCartManager; 23 | 24 | @Override 25 | public void execute(DelegateExecution ctx) throws Exception { 26 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_SHOPPINGCART, SERVICE_ACTION); 27 | BusinessEntity sc = (BusinessEntity) ctx.getVariable(ProcessConstants.VAR_SC); 28 | ServiceResponse response = shoppingCartManager.closeShoppingCart(sc); 29 | ProcessUtil.processResponse(ctx, response); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/NotifyCustomerActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer.AmqpPublisher; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 12 | 13 | 14 | @Component 15 | public class NotifyCustomerActivity implements JavaDelegate { 16 | private static final Logger LOG = LoggerFactory.getLogger(NotifyCustomerActivity.class); 17 | 18 | public static final String SERVICE_ACTION = "notify"; 19 | 20 | @Autowired 21 | AmqpPublisher amqpPubClient; 22 | 23 | @Override 24 | public void execute(DelegateExecution execution) throws Exception { 25 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_CUSTOMER, SERVICE_ACTION); 26 | BusinessEntity sc = (BusinessEntity) execution.getVariable(ProcessConstants.VAR_SC); 27 | amqpPubClient.publishEvent( 28 | ProcessUtil.buildServiceRequest(sc, ProcessConstants.SERVICE_NAME_CUSTOMER, SERVICE_ACTION)); 29 | execution.setVariable(ProcessConstants.VAR_RESPONSE, sc); 30 | ProcessContext ctx = (ProcessContext) execution.getVariable(ProcessConstants.VAR_CTX); 31 | ctx.setResponse(sc); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/PlaceOrderActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer.RpcClient; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 12 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 13 | 14 | 15 | @Component 16 | public class PlaceOrderActivity implements JavaDelegate { 17 | private static final Logger LOG = LoggerFactory.getLogger(PlaceOrderActivity.class); 18 | 19 | public static final String SERVICE_ACTION = "initiate"; 20 | 21 | @Autowired 22 | RpcClient rpcClient; 23 | 24 | @Override 25 | public void execute(DelegateExecution execution) throws Exception { 26 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_ORDER, SERVICE_ACTION); 27 | BusinessEntity sc = (BusinessEntity) execution.getVariable(ProcessConstants.VAR_SC); 28 | ServiceResponse response = rpcClient.invokeService( 29 | ProcessUtil.buildServiceRequest(sc, ProcessConstants.SERVICE_NAME_ORDER, SERVICE_ACTION)); 30 | ProcessUtil.processResponse(execution, response); 31 | execution.setVariable(ProcessConstants.VAR_ORDER_PLACED, true); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/ProcessConstants.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | public final class ProcessConstants { 4 | 5 | public static final String PROCESS_KEY_SUBMIT_SC = "submitShoppingCart"; 6 | 7 | public static final String VAR_CTX = "context"; 8 | public static final String VAR_SC_ID = "shoppingCartId"; 9 | public static final String VAR_ADDRESS = "address"; 10 | public static final String VAR_PAYMENT = "payment"; 11 | public static final String VAR_SC = "shoppingCart"; 12 | public static final String VAR_PRODUCT = "product"; 13 | public static final String VAR_RESPONSE = "response"; 14 | public static final String VAR_PAYMENT_RESERVED = "paymentReserved"; 15 | public static final String VAR_INVENTORY_ALLOCATED = "inventoryAllocated"; 16 | public static final String VAR_ORDER_PLACED = "orderPlaced"; 17 | 18 | public static final String SERVICE_NAME_LOCATION = "LocationService"; 19 | public static final String SERVICE_NAME_PAYMENT = "PaymentService"; 20 | public static final String SERVICE_NAME_INVENTORY = "InventoryService"; 21 | public static final String SERVICE_NAME_ORDER = "OrderService"; 22 | public static final String SERVICE_NAME_CUSTOMER = "CustomerService"; 23 | public static final String SERVICE_NAME_BACKOFFICE = "BackofficeService"; 24 | public static final String SERVICE_NAME_SHOPPINGCART = "ShoppingCartService"; 25 | 26 | public static final String ENTITY_TYPE_SHOPPINGCART = "SHOPPINGCART"; 27 | public static final String ENTITY_TYPE_LOCATION = "LOCATION"; 28 | public static final String ENTITY_TYPE_PAYMENT = "PAYMENT"; 29 | public static final String ENTITY_TYPE_PRODUCT = "PRODUCT"; 30 | public static final String ENTITY_TYPE_ERROR = "ERROR"; 31 | 32 | public static final String SC_STATUS_OPEN = "OPEN"; 33 | public static final String SC_STATUS_CLOSED = "CLOSED"; 34 | 35 | public static final String UNKNOWN = "UNKNOWN"; 36 | 37 | private ProcessConstants() { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/ProcessContext.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import java.io.Serializable; 4 | 5 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 6 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ErrorMessage; 7 | 8 | public class ProcessContext implements Serializable { 9 | private static final long serialVersionUID = 1L; 10 | private BusinessEntity response; 11 | 12 | public BusinessEntity getResponse() { 13 | return response; 14 | } 15 | 16 | public void setResponse(BusinessEntity response) { 17 | this.response = response; 18 | } 19 | 20 | public ErrorMessage getError() { 21 | return error; 22 | } 23 | 24 | public void setError(ErrorMessage error) { 25 | this.error = error; 26 | } 27 | 28 | private ErrorMessage error; 29 | } 30 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/ProcessUtil.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Date; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | import javax.ws.rs.core.Response; 9 | 10 | import org.camunda.bpm.engine.delegate.BpmnError; 11 | import org.camunda.bpm.engine.delegate.DelegateExecution; 12 | 13 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 14 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceRequest; 15 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 16 | 17 | public class ProcessUtil { 18 | private static final String SC_ERROR = "SC_ERROR"; 19 | 20 | private ProcessUtil() { 21 | } 22 | 23 | public static ServiceRequest buildServiceRequest(BusinessEntity shoppingCart, String serviceName, 24 | String serviceAction) { 25 | ServiceRequest sr = new ServiceRequest().withId(shoppingCart.getId()).withCreatedBy(serviceName) 26 | .withCreatedDate(new Date()).withServiceName(serviceName).withServiceAction(serviceAction); 27 | String entityType = getEntityTypeForService(serviceName); 28 | if (ProcessConstants.ENTITY_TYPE_SHOPPINGCART.equals(entityType)) { 29 | List items = new ArrayList<>(); 30 | items.add(shoppingCart); 31 | sr.setItems(items); 32 | } else { 33 | List items = shoppingCart.getRelatedEntities().stream() 34 | .filter(e -> entityType.equals(e.getEntityType())).collect(Collectors.toList()); 35 | sr.setItems(items); 36 | } 37 | return sr; 38 | } 39 | 40 | public static void processResponse(DelegateExecution ctx, ServiceResponse serviceResponse) throws Exception { 41 | ctx.setVariable(ProcessConstants.VAR_RESPONSE, serviceResponse); 42 | if (!Response.Status.OK.toString().equals(serviceResponse.getStatusCode())) { 43 | ProcessContext pctx = (ProcessContext) ctx.getVariable(ProcessConstants.VAR_CTX); 44 | pctx.setError(serviceResponse.getErrorMessage()); 45 | throw new BpmnError(SC_ERROR); 46 | } 47 | } 48 | 49 | private static String getEntityTypeForService(String serviceName) { 50 | if (ProcessConstants.SERVICE_NAME_LOCATION.equals(serviceName)) { 51 | return ProcessConstants.ENTITY_TYPE_LOCATION; 52 | } else if (ProcessConstants.SERVICE_NAME_PAYMENT.equals(serviceName)) { 53 | return ProcessConstants.ENTITY_TYPE_PAYMENT; 54 | } else if (ProcessConstants.SERVICE_NAME_INVENTORY.equals(serviceName)) { 55 | return ProcessConstants.ENTITY_TYPE_PRODUCT; 56 | } else if (ProcessConstants.SERVICE_NAME_ORDER.equals(serviceName)) { 57 | return ProcessConstants.ENTITY_TYPE_PRODUCT; 58 | } else if (ProcessConstants.SERVICE_NAME_CUSTOMER.equals(serviceName)) { 59 | return ProcessConstants.ENTITY_TYPE_SHOPPINGCART; 60 | } else { 61 | return ProcessConstants.UNKNOWN; 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/ReleaseInventoryActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer.RpcClient; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 12 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 13 | 14 | @Component 15 | public class ReleaseInventoryActivity implements JavaDelegate { 16 | private static final Logger LOG = LoggerFactory.getLogger(ReleaseInventoryActivity.class); 17 | public static final String SERVICE_ACTION = "release"; 18 | 19 | @Autowired 20 | RpcClient rpcClient; 21 | 22 | @Override 23 | public void execute(DelegateExecution execution) throws Exception { 24 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_INVENTORY, SERVICE_ACTION); 25 | BusinessEntity sc = (BusinessEntity) execution.getVariable(ProcessConstants.VAR_SC); 26 | ServiceResponse response = rpcClient.invokeService( 27 | ProcessUtil.buildServiceRequest(sc, ProcessConstants.SERVICE_NAME_INVENTORY, SERVICE_ACTION)); 28 | ProcessUtil.processResponse(execution, response); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/ReleasePaymentActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer.RpcClient; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 12 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 13 | 14 | @Component 15 | public class ReleasePaymentActivity implements JavaDelegate { 16 | private static final Logger LOG = LoggerFactory.getLogger(ReleasePaymentActivity.class); 17 | public static final String SERVICE_ACTION = "release"; 18 | 19 | @Autowired 20 | RpcClient rpcClient; 21 | 22 | @Override 23 | public void execute(DelegateExecution execution) throws Exception { 24 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_PAYMENT, SERVICE_ACTION); 25 | BusinessEntity sc = (BusinessEntity) execution.getVariable(ProcessConstants.VAR_SC); 26 | ServiceResponse response = rpcClient.invokeService( 27 | ProcessUtil.buildServiceRequest(sc, ProcessConstants.SERVICE_NAME_PAYMENT, SERVICE_ACTION)); 28 | ProcessUtil.processResponse(execution, response); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/ReservePaymentActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer.RpcClient; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 12 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 13 | 14 | @Component 15 | public class ReservePaymentActivity implements JavaDelegate { 16 | private static final Logger LOG = LoggerFactory.getLogger(ReservePaymentActivity.class); 17 | public static final String SERVICE_ACTION = "reserve"; 18 | 19 | @Autowired 20 | RpcClient rpcClient; 21 | 22 | @Override 23 | public void execute(DelegateExecution execution) throws Exception { 24 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_PAYMENT, SERVICE_ACTION); 25 | BusinessEntity sc = (BusinessEntity) execution.getVariable(ProcessConstants.VAR_SC); 26 | ServiceResponse response = rpcClient.invokeService( 27 | ProcessUtil.buildServiceRequest(sc, ProcessConstants.SERVICE_NAME_PAYMENT, SERVICE_ACTION)); 28 | ProcessUtil.processResponse(execution, response); 29 | execution.setVariable(ProcessConstants.VAR_PAYMENT_RESERVED, true); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/RetrieveShoppingCartActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 12 | import com.books.chapters.restfulapi.patterns.parttwo.dataaccess.ShoppingCartManager; 13 | 14 | @Component 15 | public class RetrieveShoppingCartActivity implements JavaDelegate { 16 | private static final Logger LOG = LoggerFactory.getLogger(RetrieveShoppingCartActivity.class); 17 | public static final String SERVICE_ACTION = "retrieve"; 18 | 19 | @Autowired 20 | ShoppingCartManager shoppingCartManager; 21 | 22 | @Override 23 | public void execute(DelegateExecution ctx) throws Exception { 24 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_SHOPPINGCART, SERVICE_ACTION); 25 | String scId = (String) ctx.getVariable(ProcessConstants.VAR_SC_ID); 26 | ServiceResponse response = shoppingCartManager.getShoppingCart(scId); 27 | ProcessUtil.processResponse(ctx, response); 28 | BusinessEntity sc = response.getItems().get(0); 29 | ctx.setVariable(ProcessConstants.VAR_SC, sc); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/SubmitErrorActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer.AmqpPublisher; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 12 | 13 | @Component 14 | public class SubmitErrorActivity implements JavaDelegate { 15 | private static final Logger LOG = LoggerFactory.getLogger(SubmitErrorActivity.class); 16 | public static final String SERVICE_ACTION = "notify"; 17 | 18 | @Autowired 19 | AmqpPublisher amqpPubClient; 20 | 21 | @Override 22 | public void execute(DelegateExecution execution) throws Exception { 23 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_BACKOFFICE, SERVICE_ACTION); 24 | ServiceResponse errorResponse = (ServiceResponse) execution.getVariable(ProcessConstants.VAR_RESPONSE); 25 | amqpPubClient.publishError(errorResponse, ProcessConstants.SERVICE_NAME_BACKOFFICE + "." + SERVICE_ACTION); 26 | ProcessContext ctx = (ProcessContext) execution.getVariable(ProcessConstants.VAR_CTX); 27 | ctx.setError(errorResponse.getErrorMessage()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/bpm/ValidateAddressActivity.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.bpm; 2 | 3 | import org.camunda.bpm.engine.delegate.DelegateExecution; 4 | import org.camunda.bpm.engine.delegate.JavaDelegate; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.amqp.producer.RpcClient; 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 12 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.ServiceResponse; 13 | 14 | @Component 15 | public class ValidateAddressActivity implements JavaDelegate { 16 | private static final Logger LOG = LoggerFactory.getLogger(ValidateAddressActivity.class); 17 | public static final String SERVICE_ACTION = "validate"; 18 | 19 | @Autowired 20 | RpcClient rpcClient; 21 | 22 | @Override 23 | public void execute(DelegateExecution execution) throws Exception { 24 | LOG.info("execute {} - {}", ProcessConstants.SERVICE_NAME_LOCATION, SERVICE_ACTION); 25 | BusinessEntity sc = (BusinessEntity) execution.getVariable(ProcessConstants.VAR_SC); 26 | ServiceResponse response = rpcClient.invokeService( 27 | ProcessUtil.buildServiceRequest(sc, ProcessConstants.SERVICE_NAME_LOCATION, SERVICE_ACTION)); 28 | ProcessUtil.processResponse(execution, response); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/dataaccess/BusinessEntityJpa.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.dataaccess; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.persistence.CascadeType; 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.FetchType; 10 | import javax.persistence.Id; 11 | import javax.persistence.JoinColumn; 12 | import javax.persistence.JoinTable; 13 | import javax.persistence.ManyToMany; 14 | import javax.persistence.Table; 15 | 16 | @Entity 17 | @Table(name = "BusinessEntity") 18 | public class BusinessEntityJpa { 19 | 20 | @Id 21 | private String id; 22 | 23 | @Column(nullable = false) 24 | private String entityType; 25 | 26 | @Column(nullable = false) 27 | private String entitySpecification; 28 | 29 | private String name; 30 | 31 | private String status; 32 | 33 | @JoinTable(name = "entityRelation", joinColumns = { 34 | @JoinColumn(name = "referencingEntity", referencedColumnName = "id", nullable = false) }, inverseJoinColumns = { 35 | @JoinColumn(name = "referencedEntity", referencedColumnName = "id", nullable = false) }) 36 | @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) 37 | private List relatedEntities = new ArrayList(); 38 | 39 | public String getId() { 40 | return id; 41 | } 42 | 43 | public void setId(String id) { 44 | this.id = id; 45 | } 46 | 47 | public BusinessEntityJpa withId(String id) { 48 | this.id = id; 49 | return this; 50 | } 51 | 52 | public String getEntityType() { 53 | return entityType; 54 | } 55 | 56 | public void setEntityType(String entityType) { 57 | this.entityType = entityType; 58 | } 59 | 60 | public BusinessEntityJpa withEntityType(String entityType) { 61 | this.entityType = entityType; 62 | return this; 63 | } 64 | 65 | public String getEntitySpecification() { 66 | return entitySpecification; 67 | } 68 | 69 | public void setEntitySpecification(String entitySpecification) { 70 | this.entitySpecification = entitySpecification; 71 | } 72 | 73 | public BusinessEntityJpa withEntitySpecification(String entitySpecification) { 74 | this.entitySpecification = entitySpecification; 75 | return this; 76 | } 77 | 78 | public String getName() { 79 | return name; 80 | } 81 | 82 | public void setName(String name) { 83 | this.name = name; 84 | } 85 | 86 | public BusinessEntityJpa withName(String name) { 87 | this.name = name; 88 | return this; 89 | } 90 | 91 | public String getStatus() { 92 | return status; 93 | } 94 | 95 | public void setStatus(String status) { 96 | this.status = status; 97 | } 98 | 99 | public BusinessEntityJpa withStatus(String status) { 100 | this.status = status; 101 | return this; 102 | } 103 | 104 | public List getRelatedEntities() { 105 | return relatedEntities; 106 | } 107 | 108 | public void setRelatedEntities(List relatedEntities) { 109 | this.relatedEntities = relatedEntities; 110 | } 111 | 112 | public BusinessEntityJpa withRelatedEntities(List relatedEntities) { 113 | this.relatedEntities = relatedEntities; 114 | return this; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/dataaccess/BusinessEntityRepository.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.dataaccess; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface BusinessEntityRepository extends CrudRepository { 6 | } 7 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/dataaccess/BusinessEntityTranslator.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.dataaccess; 2 | 3 | import java.util.Collection; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.Objects; 7 | import java.util.Optional; 8 | import java.util.function.Function; 9 | import java.util.stream.Collectors; 10 | 11 | import com.books.chapters.restfulapi.patterns.parttwo.adapter.entity.BusinessEntity; 12 | 13 | public class BusinessEntityTranslator { 14 | private BusinessEntityTranslator() { 15 | } 16 | 17 | public static BusinessEntity fromJpa(BusinessEntityJpa jpaEntity) { 18 | if (jpaEntity == null) { 19 | return null; 20 | } 21 | 22 | return new BusinessEntity().withId(jpaEntity.getId()).withName(jpaEntity.getName()) 23 | .withStatus(jpaEntity.getStatus()).withEntitySpecification(jpaEntity.getEntitySpecification()) 24 | .withEntityType(jpaEntity.getEntityType()).withRelatedEntities( 25 | translateToList(jpaEntity.getRelatedEntities(), BusinessEntityTranslator::fromJpa)); 26 | } 27 | 28 | public static BusinessEntityJpa toJpa(BusinessEntity entity) { 29 | if (entity == null) { 30 | return null; 31 | } 32 | 33 | return new BusinessEntityJpa().withId(entity.getId()).withName(entity.getName()).withStatus(entity.getStatus()) 34 | .withEntitySpecification(entity.getEntitySpecification()).withEntityType(entity.getEntityType()) 35 | .withRelatedEntities(translateToList(entity.getRelatedEntities(), BusinessEntityTranslator::toJpa)); 36 | } 37 | 38 | public static List translateToList(Collection froms, Function translate) { 39 | return Optional.ofNullable(froms).orElse(Collections.emptyList()).stream().map(translate) 40 | .filter(Objects::nonNull).collect(Collectors.toList()); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/java/com/books/chapters/restfulapi/patterns/parttwo/rest/ShoppingCartRestController.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo.rest; 2 | 3 | import org.camunda.bpm.engine.ProcessEngine; 4 | import org.camunda.bpm.engine.runtime.ProcessInstance; 5 | import org.camunda.bpm.engine.variable.Variables; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestMethod; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import com.books.chapters.restfulapi.patterns.parttwo.bpm.ProcessConstants; 15 | import com.books.chapters.restfulapi.patterns.parttwo.bpm.ProcessContext; 16 | 17 | @RestController 18 | @RequestMapping("/shoppingCart") 19 | public class ShoppingCartRestController { 20 | 21 | @Autowired 22 | private ProcessEngine camunda; 23 | 24 | @RequestMapping(value = "/{scId}/submit", method = RequestMethod.POST) 25 | public ResponseEntity placeOrderPOST(@PathVariable("scId") String scId) { 26 | ProcessContext context = new ProcessContext(); 27 | submitShoppingCart(scId, context); 28 | if (context.getError() != null) { 29 | return new ResponseEntity<>(context.getError(), HttpStatus.FORBIDDEN); 30 | } 31 | return new ResponseEntity<>(context.getResponse(), HttpStatus.OK); 32 | 33 | } 34 | 35 | private ProcessInstance submitShoppingCart(String scId, ProcessContext context) { 36 | return camunda.getRuntimeService().startProcessInstanceByKey(// 37 | "submitShoppingCart", // 38 | Variables // 39 | .putValue(ProcessConstants.VAR_SC_ID, scId).putValue(ProcessConstants.VAR_CTX, context)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/resources/META-INF/processes.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices/92544a10bee84b777c98b3fc6cd38a5a8e886066/Chapter07/event-driven-service-orchestration/shopping-cart/src/main/resources/META-INF/processes.xml -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | camunda.bpm: 2 | admin-user: 3 | id: admin 4 | password: admin 5 | firstName: Admin 6 | filter: 7 | create: All tasks 8 | 9 | spring: 10 | datasource: 11 | url: jdbc:h2:~/camunda;DB_CLOSE_ON_EXIT=false 12 | rabbitmq: 13 | host: localhost 14 | port: 5777 15 | username: test 16 | password: test 17 | exchange: 18 | rpc: shoppingcart.rpc 19 | pub: shoppingcart.pub -------------------------------------------------------------------------------- /Chapter07/event-driven-service-orchestration/shopping-cart/src/test/java/com/books/chapters/restfulapi/patterns/parttwo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.parttwo; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /investor-services/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.books.chapters.restfulapi.patterns.partOne 7 | investor-services 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | investor-services 12 | sample spring boot application for the the restful API patterns chapter part one 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.4.4.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | com.fasterxml.jackson.dataformat 38 | jackson-dataformat-xml 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-maven-plugin 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /investor-services/scripts/buildMyExamples.bat: -------------------------------------------------------------------------------- 1 | mkdir bookexamples 2 | cd bookexamples 3 | mkdir ch03 4 | cd ch03 5 | git clone https://github.com/PacktPublishing/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices.git 6 | cd Hands-On-RESTful-API-Design-Patterns-and-Best-Practices 7 | cd investor-services 8 | mvn clean package -------------------------------------------------------------------------------- /investor-services/scripts/runMyExamples.bat: -------------------------------------------------------------------------------- 1 | java -jar bookexamples/ch03/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices/investor-services/target/investor-services-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/InvestorServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class InvestorServicesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(InvestorServicesApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/models/IndividualInvestorPortfolio.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.models; 2 | 3 | public class IndividualInvestorPortfolio { 4 | private String investorId; 5 | private int stocksHoldCount; 6 | 7 | public IndividualInvestorPortfolio(String investorId, int stocksHoldCount){ 8 | this.investorId = investorId; 9 | this.stocksHoldCount = stocksHoldCount; 10 | } 11 | 12 | public String getInvestorId() { 13 | return investorId; 14 | } 15 | public void setInvestorId(String investorId) { 16 | this.investorId = investorId; 17 | } 18 | public int getStocksHoldCount() { 19 | return stocksHoldCount; 20 | } 21 | public void setStocksHoldCount(int stocksCount) { 22 | stocksHoldCount = stocksCount; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/models/Investor.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.models; 2 | 3 | import java.util.List; 4 | 5 | public class Investor { 6 | private String id; 7 | private String name; 8 | private String description; 9 | private List stocks; 10 | 11 | public Investor(String id, String name, String description, List stocks) { 12 | this.id = id; 13 | this.name = name; 14 | this.description = description; 15 | this.stocks = stocks; 16 | } 17 | 18 | public String getId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public void setDescription(String description) { 39 | this.description = description; 40 | } 41 | 42 | public List getStocks() { 43 | return stocks; 44 | } 45 | 46 | public void setStocks(List stocks) { 47 | this.stocks = stocks; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return String.format("Investor [id=%s, name=%s, description=%s, stocks=%s]", id, name, description, stocks); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/models/Stock.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.models; 2 | 3 | public class Stock { 4 | private String symbol; 5 | private int numberOfSharesHeld; 6 | private double tickerPrice = 0.0; 7 | 8 | Stock() { 9 | // default constructor, otherwise the spring boot runtime complains that 10 | // it can not instantiate this Stock class when we try to do POST to 11 | // insert a new Stock from the controller classes 12 | } 13 | 14 | public Stock(String symbol, int numberOfSharesHeld, double tickerPrice) { 15 | 16 | this.symbol = symbol; 17 | this.numberOfSharesHeld = numberOfSharesHeld; 18 | this.tickerPrice = tickerPrice; 19 | } 20 | 21 | public String getSymbol() { 22 | return symbol; 23 | } 24 | 25 | public void setSymbol(String symbol) { 26 | this.symbol = symbol; 27 | } 28 | 29 | public int getNumberOfSharesHeld() { 30 | return numberOfSharesHeld; 31 | } 32 | 33 | public void setNumberOfSharesHeld(int numberOfShares) { 34 | this.numberOfSharesHeld = numberOfShares; 35 | } 36 | 37 | public double getTickerPrice() { 38 | return tickerPrice; 39 | } 40 | 41 | public void setTickerPrice(double tickerPrice) { 42 | this.tickerPrice = tickerPrice; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | String pattern = "Stock [symbol: %s, numberOfSharesHeld: %d, tickerPrice: %6.2f]"; 48 | return String.format(pattern, symbol, numberOfSharesHeld, tickerPrice); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /investor-services/src/main/java/com/books/chapters/restfulapi/patterns/chap3/springboot/models/errorsandexceptions/InvestorNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.models.errorsandexceptions; 2 | 3 | public class InvestorNotFoundException extends RuntimeException{ 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public InvestorNotFoundException(String exception) 8 | { 9 | super(exception); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /investor-services/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=9090 -------------------------------------------------------------------------------- /investor-services/src/test/java/com/books/chapters/restfulapi/patterns/chap3/springboot/InvestorServicesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class InvestorServicesApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /investor-services/src/test/java/com/books/chapters/restfulapi/patterns/chap3/springboot/controller/InvestorControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.books.chapters.restfulapi.patterns.chap3.springboot.controller; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.http.MediaType; 9 | import org.springframework.mock.web.MockHttpServletResponse; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | import org.springframework.test.web.servlet.MockMvc; 12 | import org.springframework.test.web.servlet.MvcResult; 13 | import org.springframework.test.web.servlet.RequestBuilder; 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 15 | 16 | import com.books.chapters.restfulapi.patterns.chap3.springboot.controller.InvestorController; 17 | import com.books.chapters.restfulapi.patterns.chap3.springboot.service.InvestorService; 18 | 19 | @RunWith(SpringRunner.class) 20 | @WebMvcTest(value = InvestorController.class, secure = false) 21 | public class InvestorControllerTest { 22 | 23 | @Autowired 24 | private MockMvc mockMvc; 25 | 26 | @MockBean 27 | private InvestorService studentService; 28 | 29 | @Test 30 | public void fetchAllInvestors() throws Exception{ 31 | RequestBuilder requestBuilder = MockMvcRequestBuilders.get( 32 | "/investors").accept( 33 | MediaType.APPLICATION_JSON); 34 | MvcResult result = mockMvc.perform(requestBuilder).andReturn(); 35 | MockHttpServletResponse response = result.getResponse(); 36 | System.out.println("here "+response); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /scripts/buildMyExamples.bat: -------------------------------------------------------------------------------- 1 | git config --system core.longpaths true 2 | git clone https://github.com/PacktPublishing/Hands-On-RESTful-API-Design-Patterns-and-Best-Practices.git 3 | cd Hands-On-RESTful-API-Design-Patterns-and-Best-Practices\chapter-3\investor-services 4 | mvn clean package 5 | -------------------------------------------------------------------------------- /scripts/runMyExamples.bat: -------------------------------------------------------------------------------- 1 | java -jar Hands-On-RESTful-API-Design-Patterns-and-Best-Practices/chapter-3/investor-services/target/investor-services-0.0.1-SNAPSHOT.jar 2 | --------------------------------------------------------------------------------