From 9d9124fb9a89d42879eaa7d6c2afbbe895ae76a4 Mon Sep 17 00:00:00 2001 From: Ranga Rao Karanam Date: Sat, 9 Sep 2017 17:12:55 +0530 Subject: [PATCH] Step23 --- .../currency-conversion-service/pom.xml | 11 + .../CurrencyConversionController.java | 38 +- .../CurrencyConversionServiceApplication.java | 2 + .../CurrencyExchangeServiceProxy.java | 15 + .../src/main/resources/application.properties | 3 +- 03.microservices/step23.md | 1109 +++++++++++++++++ 03.microservices/step23.zip | Bin 0 -> 33164 bytes 7 files changed, 1164 insertions(+), 14 deletions(-) create mode 100644 03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyExchangeServiceProxy.java create mode 100644 03.microservices/step23.md create mode 100644 03.microservices/step23.zip diff --git a/03.microservices/currency-conversion-service/pom.xml b/03.microservices/currency-conversion-service/pom.xml index ae0d648c..dbf98db9 100644 --- a/03.microservices/currency-conversion-service/pom.xml +++ b/03.microservices/currency-conversion-service/pom.xml @@ -34,6 +34,17 @@ org.springframework.cloud spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-feign + + + + org.springframework.cloud + spring-cloud-starter-ribbon + + org.springframework.boot spring-boot-starter-web diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java index 4a3aa63e..036a54c8 100644 --- a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -12,26 +13,37 @@ @RestController public class CurrencyConversionController { - + + @Autowired + private CurrencyExchangeServiceProxy proxy; + @GetMapping("/currency-converter/from/{from}/to/{to}/quantity/{quantity}") - public CurrencyConversionBean convertCurrency(@PathVariable String from, - @PathVariable String to, - @PathVariable BigDecimal quantity - ){ - + public CurrencyConversionBean convertCurrency(@PathVariable String from, @PathVariable String to, + @PathVariable BigDecimal quantity) { + + // Feign - Problem 1 Map uriVariables = new HashMap<>(); uriVariables.put("from", from); uriVariables.put("to", to); ResponseEntity responseEntity = new RestTemplate().getForEntity( - "http://localhost:8000/currency-exchange/from/{from}/to/{to}", - CurrencyConversionBean.class, - uriVariables ); - + "http://localhost:8000/currency-exchange/from/{from}/to/{to}", CurrencyConversionBean.class, + uriVariables); + CurrencyConversionBean response = responseEntity.getBody(); - - return new CurrencyConversionBean(response.getId(),from,to,response.getConversionMultiple(), - quantity,quantity.multiply(response.getConversionMultiple()),response.getPort()); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); + } + + @GetMapping("/currency-converter-feign/from/{from}/to/{to}/quantity/{quantity}") + public CurrencyConversionBean convertCurrencyFeign(@PathVariable String from, @PathVariable String to, + @PathVariable BigDecimal quantity) { + + CurrencyConversionBean response = proxy.retrieveExchangeValue(from, to); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); } } diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java index 532dd3b4..6042ed2e 100644 --- a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.feign.EnableFeignClients; @SpringBootApplication +@EnableFeignClients("com.in28minutes.microservices.currencyconversionservice") public class CurrencyConversionServiceApplication { public static void main(String[] args) { diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyExchangeServiceProxy.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyExchangeServiceProxy.java new file mode 100644 index 00000000..2e24d47e --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyExchangeServiceProxy.java @@ -0,0 +1,15 @@ +package com.in28minutes.microservices.currencyconversionservice; + +import org.springframework.cloud.netflix.feign.FeignClient; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +//@FeignClient(name="currency-exchange-service", url="localhost:8000") +@FeignClient(name="currency-exchange-service") +@RibbonClient(name="currency-exchange-service") +public interface CurrencyExchangeServiceProxy { + @GetMapping("/currency-exchange/from/{from}/to/{to}") + public CurrencyConversionBean retrieveExchangeValue + (@PathVariable("from") String from, @PathVariable("to") String to); +} diff --git a/03.microservices/currency-conversion-service/src/main/resources/application.properties b/03.microservices/currency-conversion-service/src/main/resources/application.properties index e1042fe2..769573e5 100644 --- a/03.microservices/currency-conversion-service/src/main/resources/application.properties +++ b/03.microservices/currency-conversion-service/src/main/resources/application.properties @@ -1,2 +1,3 @@ spring.application.name=currency-conversion-service -server.port=8100 \ No newline at end of file +server.port=8100 +currency-exchange-service.ribbon.listOfServers=http://localhost:8000,http://localhost:8001 \ No newline at end of file diff --git a/03.microservices/step23.md b/03.microservices/step23.md new file mode 100644 index 00000000..fa485c2c --- /dev/null +++ b/03.microservices/step23.md @@ -0,0 +1,1109 @@ + +## Complete Code Example + + +### /currency-conversion-service/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + currency-conversion-service + 0.0.1-SNAPSHOT + jar + + currency-conversion-service + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-feign + + + + org.springframework.cloud + spring-cloud-starter-ribbon + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionBean.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import java.math.BigDecimal; + +public class CurrencyConversionBean { + private Long id; + private String from; + private String to; + private BigDecimal conversionMultiple; + private BigDecimal quantity; + private BigDecimal totalCalculatedAmount; + private int port; + + public CurrencyConversionBean() { + + } + + public CurrencyConversionBean(Long id, String from, String to, BigDecimal conversionMultiple, BigDecimal quantity, + BigDecimal totalCalculatedAmount, int port) { + super(); + this.id = id; + this.from = from; + this.to = to; + this.conversionMultiple = conversionMultiple; + this.quantity = quantity; + this.totalCalculatedAmount = totalCalculatedAmount; + this.port = port; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public BigDecimal getConversionMultiple() { + return conversionMultiple; + } + + public void setConversionMultiple(BigDecimal conversionMultiple) { + this.conversionMultiple = conversionMultiple; + } + + public BigDecimal getQuantity() { + return quantity; + } + + public void setQuantity(BigDecimal quantity) { + this.quantity = quantity; + } + + public BigDecimal getTotalCalculatedAmount() { + return totalCalculatedAmount; + } + + public void setTotalCalculatedAmount(BigDecimal totalCalculatedAmount) { + this.totalCalculatedAmount = totalCalculatedAmount; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + +} +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +@RestController +public class CurrencyConversionController { + + @Autowired + private CurrencyExchangeServiceProxy proxy; + + @GetMapping("/currency-converter/from/{from}/to/{to}/quantity/{quantity}") + public CurrencyConversionBean convertCurrency(@PathVariable String from, @PathVariable String to, + @PathVariable BigDecimal quantity) { + + // Feign - Problem 1 + Map uriVariables = new HashMap<>(); + uriVariables.put("from", from); + uriVariables.put("to", to); + + ResponseEntity responseEntity = new RestTemplate().getForEntity( + "http://localhost:8000/currency-exchange/from/{from}/to/{to}", CurrencyConversionBean.class, + uriVariables); + + CurrencyConversionBean response = responseEntity.getBody(); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); + } + + @GetMapping("/currency-converter-feign/from/{from}/to/{to}/quantity/{quantity}") + public CurrencyConversionBean convertCurrencyFeign(@PathVariable String from, @PathVariable String to, + @PathVariable BigDecimal quantity) { + + CurrencyConversionBean response = proxy.retrieveExchangeValue(from, to); + + return new CurrencyConversionBean(response.getId(), from, to, response.getConversionMultiple(), quantity, + quantity.multiply(response.getConversionMultiple()), response.getPort()); + } + +} +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.feign.EnableFeignClients; + +@SpringBootApplication +@EnableFeignClients("com.in28minutes.microservices.currencyconversionservice") +public class CurrencyConversionServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyConversionServiceApplication.class, args); + } +} +``` +--- + +### /currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyExchangeServiceProxy.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import org.springframework.cloud.netflix.feign.FeignClient; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +//@FeignClient(name="currency-exchange-service", url="localhost:8000") +@FeignClient(name="currency-exchange-service") +@RibbonClient(name="currency-exchange-service") +public interface CurrencyExchangeServiceProxy { + @GetMapping("/currency-exchange/from/{from}/to/{to}") + public CurrencyConversionBean retrieveExchangeValue + (@PathVariable("from") String from, @PathVariable("to") String to); +} +``` +--- + +### /currency-conversion-service/src/main/resources/application.properties + +```properties +spring.application.name=currency-conversion-service +server.port=8100 +currency-exchange-service.ribbon.listOfServers=http://localhost:8000,http://localhost:8001 +``` +--- + +### /currency-conversion-service/src/test/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplicationTests.java + +```java +package com.in28minutes.microservices.currencyconversionservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class CurrencyConversionServiceApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /currency-exchange-service/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + currency-exchange-service + 0.0.1-SNAPSHOT + jar + + currency-exchange-service + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeController.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CurrencyExchangeController { + + @Autowired + private Environment environment; + + @Autowired + private ExchangeValueRepository repository; + + @GetMapping("/currency-exchange/from/{from}/to/{to}") + public ExchangeValue retrieveExchangeValue + (@PathVariable String from, @PathVariable String to){ + + ExchangeValue exchangeValue = + repository.findByFromAndTo(from, to); + + exchangeValue.setPort( + Integer.parseInt(environment.getProperty("local.server.port"))); + + return exchangeValue; + } +} +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplication.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class CurrencyExchangeServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CurrencyExchangeServiceApplication.class, args); + } +} +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/ExchangeValue.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import java.math.BigDecimal; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class ExchangeValue { + + @Id + private Long id; + + @Column(name="currency_from") + private String from; + + @Column(name="currency_to") + private String to; + + private BigDecimal conversionMultiple; + private int port; + + public ExchangeValue() { + + } + + + public ExchangeValue(Long id, String from, String to, BigDecimal conversionMultiple) { + super(); + this.id = id; + this.from = from; + this.to = to; + this.conversionMultiple = conversionMultiple; + } + + public Long getId() { + return id; + } + + public String getFrom() { + return from; + } + + public String getTo() { + return to; + } + + public BigDecimal getConversionMultiple() { + return conversionMultiple; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + +} +``` +--- + +### /currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/ExchangeValueRepository.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ExchangeValueRepository extends + JpaRepository{ + ExchangeValue findByFromAndTo(String from, String to); +} +``` +--- + +### /currency-exchange-service/src/main/resources/application.properties + +```properties +spring.application.name=currency-exchange-service +server.port=8000 + +spring.jpa.show-sql=true +spring.h2.console.enabled=true +``` +--- + +### /currency-exchange-service/src/main/resources/data.sql + +``` +insert into exchange_value(id,currency_from,currency_to,conversion_multiple,port) +values(10001,'USD','INR',65,0); +insert into exchange_value(id,currency_from,currency_to,conversion_multiple,port) +values(10002,'EUR','INR',75,0); +insert into exchange_value(id,currency_from,currency_to,conversion_multiple,port) +values(10003,'AUD','INR',25,0); +``` +--- + +### /currency-exchange-service/src/test/java/com/in28minutes/microservices/currencyexchangeservice/CurrencyExchangeServiceApplicationTests.java + +```java +package com.in28minutes.microservices.currencyexchangeservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class CurrencyExchangeServiceApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /git-localconfig-repo/limits-service-dev.properties + +```properties +limits-service.minimum=1 +``` +--- + +### /git-localconfig-repo/limits-service-qa.properties + +```properties +limits-service.minimum=2 +limits-service.maximum=222 +``` +--- + +### /git-localconfig-repo/limits-service.properties + +```properties +limits-service.minimum=8 +limits-service.maximum=888 +``` +--- + +### /limits-service/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + limits-service + 0.0.1-SNAPSHOT + jar + + limits-service + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/bean/LimitConfiguration.java + +```java +package com.in28minutes.microservices.limitsservice.bean; + +public class LimitConfiguration { + private int maximum; + private int minimum; + + protected LimitConfiguration() { + + } + + public LimitConfiguration(int maximum, int minimum) { + super(); + this.maximum = maximum; + this.minimum = minimum; + } + + public int getMaximum() { + return maximum; + } + + public int getMinimum() { + return minimum; + } + +} +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/Configuration.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties("limits-service") +public class Configuration { + + private int minimum; + private int maximum; + + public void setMinimum(int minimum) { + this.minimum = minimum; + } + + public void setMaximum(int maximum) { + this.maximum = maximum; + } + + public int getMinimum() { + return minimum; + } + + public int getMaximum() { + return maximum; + } + +} +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsConfigurationController.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.in28minutes.microservices.limitsservice.bean.LimitConfiguration; + +@RestController +public class LimitsConfigurationController { + + @Autowired + private Configuration configuration; + + @GetMapping("/limits") + public LimitConfiguration retrieveLimitsFromConfigurations() { + return new LimitConfiguration(configuration.getMaximum(), + configuration.getMinimum()); + } + +} +``` +--- + +### /limits-service/src/main/java/com/in28minutes/microservices/limitsservice/LimitsServiceApplication.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LimitsServiceApplication { + public static void main(String[] args) { + SpringApplication.run(LimitsServiceApplication.class, args); + } +} +``` +--- + +### /limits-service/src/main/resources/bootstrap.properties + +```properties +spring.application.name=limits-service +spring.cloud.config.uri=http://localhost:8888 +spring.profiles.active=qa +``` +--- + +### /limits-service/src/test/java/com/in28minutes/microservices/limitsservice/LimitsServiceApplicationTests.java + +```java +package com.in28minutes.microservices.limitsservice; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class LimitsServiceApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- + +### /spring-cloud-config-server/pom.xml + +```xml + + + 4.0.0 + + com.in28minutes.microservices + spring-cloud-config-server + 0.0.1-SNAPSHOT + jar + + spring-cloud-config-server + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M3 + + + + + UTF-8 + UTF-8 + 1.8 + Finchley.M2 + + + + + org.springframework.cloud + spring-cloud-config-server + + + + org.springframework.boot + spring-boot-devtools + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + +``` +--- + +### /spring-cloud-config-server/src/main/java/com/in28minutes/microservices/springcloudconfigserver/SpringCloudConfigServerApplication.java + +```java +package com.in28minutes.microservices.springcloudconfigserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; + +@EnableConfigServer +@SpringBootApplication +public class SpringCloudConfigServerApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudConfigServerApplication.class, args); + } +} +``` +--- + +### /spring-cloud-config-server/src/main/resources/application.properties + +```properties +spring.application.name=spring-cloud-config-server +server.port=8888 +spring.cloud.config.server.git.uri=file:///in28Minutes/git/spring-micro-services/03.microservices/git-localconfig-repo +``` +--- + +### /spring-cloud-config-server/src/test/java/com/in28minutes/microservices/springcloudconfigserver/SpringCloudConfigServerApplicationTests.java + +```java +package com.in28minutes.microservices.springcloudconfigserver; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class SpringCloudConfigServerApplicationTests { + + @Test + public void contextLoads() { + } + +} +``` +--- diff --git a/03.microservices/step23.zip b/03.microservices/step23.zip new file mode 100644 index 0000000000000000000000000000000000000000..5afe78a68e4ab137b84de77bc8e5b2fdb63e1f94 GIT binary patch literal 33164 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 KL^Vaw-~JDK!Ll3x literal 0 HcmV?d00001