#297 Create Spring Boot-backed API Gateway that aggregates calls to the Image and Price microservices
This commit is contained in:
parent
01737bc643
commit
665256ecc0
@ -9,11 +9,66 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>api-gateway</artifactId>
|
<artifactId>api-gateway</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<spring.version>4.2.5.RELEASE</spring.version>
|
||||||
|
<spring-boot.version>1.3.3.RELEASE</spring-boot.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-dependencies</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>4.5.2</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
@ -1,21 +1,31 @@
|
|||||||
package com.iluwatar.api.gateway;
|
package com.iluwatar.api.gateway;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ApiGateway aggregates calls to microservices based on the needs of the individual clients.
|
* The ApiGateway aggregates calls to microservices based on the needs of the individual clients.
|
||||||
*/
|
*/
|
||||||
|
@RestController
|
||||||
public class ApiGateway {
|
public class ApiGateway {
|
||||||
|
|
||||||
private ImageService imageService = new ImageService();
|
@Resource
|
||||||
private PriceService priceService = new PriceService();
|
private ImageClient imageClient;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PriceClient priceClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves product information that desktop clients need
|
* Retrieves product information that desktop clients need
|
||||||
* @return Product information for clients on a desktop
|
* @return Product information for clients on a desktop
|
||||||
*/
|
*/
|
||||||
|
@RequestMapping("/desktop")
|
||||||
public DesktopProduct getProductDesktop() {
|
public DesktopProduct getProductDesktop() {
|
||||||
DesktopProduct desktopProduct = new DesktopProduct();
|
DesktopProduct desktopProduct = new DesktopProduct();
|
||||||
desktopProduct.setImagePath(imageService.getImagePath());
|
desktopProduct.setImagePath(imageClient.getImagePath());
|
||||||
desktopProduct.setPrice(priceService.getPrice());
|
desktopProduct.setPrice(priceClient.getPrice());
|
||||||
return desktopProduct;
|
return desktopProduct;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,9 +33,10 @@ public class ApiGateway {
|
|||||||
* Retrieves product information that mobile clients need
|
* Retrieves product information that mobile clients need
|
||||||
* @return Product information for clients on a mobile device
|
* @return Product information for clients on a mobile device
|
||||||
*/
|
*/
|
||||||
|
@RequestMapping("/mobile")
|
||||||
public MobileProduct getProductMobile() {
|
public MobileProduct getProductMobile() {
|
||||||
MobileProduct mobileProduct = new MobileProduct();
|
MobileProduct mobileProduct = new MobileProduct();
|
||||||
mobileProduct.setPrice(priceService.getPrice());
|
mobileProduct.setPrice(priceClient.getPrice());
|
||||||
return mobileProduct;
|
return mobileProduct;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package com.iluwatar.api.gateway;
|
package com.iluwatar.api.gateway;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* With the Microservices pattern, a client may need data from multiple different microservices.
|
* With the Microservices pattern, a client may need data from multiple different microservices.
|
||||||
* If the client called each microservice directly, that could contribute to longer load times,
|
* If the client called each microservice directly, that could contribute to longer load times,
|
||||||
@ -18,13 +21,15 @@ package com.iluwatar.api.gateway;
|
|||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This implementation shows what the API Gateway pattern could look like for an e-commerce site.
|
* This implementation shows what the API Gateway pattern could look like for an e-commerce site.
|
||||||
* In this case, the (@link ImageService) and (@link PriceService) represent our microservices.
|
* The {@link ApiGateway} makes calls to the Image and Price microservices using the
|
||||||
* Customers viewing the site on a desktop device can see both price information and an image of
|
* {@link ImageClientImpl} and {@link PriceClientImpl} respectively. Customers viewing the site on a
|
||||||
* a product, so the (@link ApiGateway) calls both of the microservices and aggregates the data in
|
* desktop device can see both price information and an image of a product, so the {@link ApiGateway}
|
||||||
* the (@link DesktopProduct) model. However, mobile users only see price information; they do not
|
* calls both of the microservices and aggregates the data in the {@link DesktopProduct} model.
|
||||||
* see a product image. For mobile users, the (@link ApiGateway) only retrieves price information,
|
* However, mobile users only see price information; they do not see a product image. For mobile
|
||||||
* which it uses to populate the (@link MobileProduct).
|
* users, the {@link ApiGateway} only retrieves price information, which it uses to populate the
|
||||||
|
* {@link MobileProduct}.
|
||||||
*/
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,13 +39,6 @@ public class App {
|
|||||||
* command line args
|
* command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ApiGateway apiGateway = new ApiGateway();
|
SpringApplication.run(App.class, args);
|
||||||
|
|
||||||
DesktopProduct desktopProduct = apiGateway.getProductDesktop();
|
|
||||||
System.out.println(String.format("Desktop Product \nPrice: %s\nImage path: %s",
|
|
||||||
desktopProduct.getPrice(), desktopProduct.getImagePath()));
|
|
||||||
|
|
||||||
MobileProduct mobileProduct = apiGateway.getProductMobile();
|
|
||||||
System.out.println(String.format("Mobile Product \nPrice: %s", mobileProduct.getPrice()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,14 @@ package com.iluwatar.api.gateway;
|
|||||||
* Encapsulates all of the information that a desktop client needs to display a product.
|
* Encapsulates all of the information that a desktop client needs to display a product.
|
||||||
*/
|
*/
|
||||||
public class DesktopProduct {
|
public class DesktopProduct {
|
||||||
|
/**
|
||||||
|
* The price of the product
|
||||||
|
*/
|
||||||
private String price;
|
private String price;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to the image of the product
|
||||||
|
*/
|
||||||
private String imagePath;
|
private String imagePath;
|
||||||
|
|
||||||
public String getPrice() {
|
public String getPrice() {
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.iluwatar.api.gateway;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface used to communicate with the Image microservice
|
||||||
|
*/
|
||||||
|
public interface ImageClient {
|
||||||
|
String getImagePath();
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.iluwatar.api.gateway;
|
||||||
|
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An adapter to communicate with the Image microservice
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ImageClientImpl implements ImageClient{
|
||||||
|
/**
|
||||||
|
* Makes a simple HTTP Get request to the Image microservice
|
||||||
|
* @return The path to the image
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getImagePath() {
|
||||||
|
String response = null;
|
||||||
|
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:50005/image-path");
|
||||||
|
try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
|
||||||
|
response = EntityUtils.toString(httpResponse.getEntity());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
package com.iluwatar.api.gateway;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a microservice used to retrieve image data.
|
|
||||||
*/
|
|
||||||
public class ImageService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the image path
|
|
||||||
* @return the image path
|
|
||||||
*/
|
|
||||||
public String getImagePath() {
|
|
||||||
return "/product-image.png";
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,9 @@ package com.iluwatar.api.gateway;
|
|||||||
* Encapsulates all of the information that mobile client needs to display a product.
|
* Encapsulates all of the information that mobile client needs to display a product.
|
||||||
*/
|
*/
|
||||||
public class MobileProduct {
|
public class MobileProduct {
|
||||||
|
/**
|
||||||
|
* The price of the product
|
||||||
|
*/
|
||||||
private String price;
|
private String price;
|
||||||
|
|
||||||
public String getPrice() {
|
public String getPrice() {
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.iluwatar.api.gateway;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface used to communicate with the Price microservice
|
||||||
|
*/
|
||||||
|
public interface PriceClient {
|
||||||
|
String getPrice();
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.iluwatar.api.gateway;
|
||||||
|
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An adapter to communicate with the Price microservice
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class PriceClientImpl implements PriceClient{
|
||||||
|
/**
|
||||||
|
* Makes a simple HTTP Get request to the Price microservice
|
||||||
|
* @return The price of the product
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getPrice() {
|
||||||
|
String response = null;
|
||||||
|
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:50006/price");
|
||||||
|
try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
|
||||||
|
response = EntityUtils.toString(httpResponse.getEntity());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +0,0 @@
|
|||||||
package com.iluwatar.api.gateway;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a microservice used to retrieve price data.
|
|
||||||
*/
|
|
||||||
public class PriceService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the price
|
|
||||||
* @return the price
|
|
||||||
*/
|
|
||||||
public String getPrice() {
|
|
||||||
return "20";
|
|
||||||
}
|
|
||||||
}
|
|
1
api-gateway/src/main/resources/application.properties
Normal file
1
api-gateway/src/main/resources/application.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
server.port=50004
|
@ -1,21 +1,44 @@
|
|||||||
package com.iluwatar.api.gateway;
|
package com.iluwatar.api.gateway;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class ApiGatewayTest {
|
public class ApiGatewayTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private ApiGateway apiGateway;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ImageClient imageClient;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PriceClient priceClient;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests getting the data for a desktop client
|
* Tests getting the data for a desktop client
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetProductDesktop() {
|
public void testGetProductDesktop() {
|
||||||
ApiGateway apiGateway = new ApiGateway();
|
String imagePath = "/product-image.png";
|
||||||
|
String price = "20";
|
||||||
|
when(imageClient.getImagePath()).thenReturn(imagePath);
|
||||||
|
when(priceClient.getPrice()).thenReturn(price);
|
||||||
|
|
||||||
DesktopProduct desktopProduct = apiGateway.getProductDesktop();
|
DesktopProduct desktopProduct = apiGateway.getProductDesktop();
|
||||||
|
|
||||||
assertEquals("20", desktopProduct.getPrice());
|
assertEquals(price, desktopProduct.getPrice());
|
||||||
assertEquals("/product-image.png", desktopProduct.getImagePath());
|
assertEquals(imagePath, desktopProduct.getImagePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,9 +46,11 @@ public class ApiGatewayTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetProductMobile() {
|
public void testGetProductMobile() {
|
||||||
ApiGateway apiGateway = new ApiGateway();
|
String price = "20";
|
||||||
|
when(priceClient.getPrice()).thenReturn(price);
|
||||||
|
|
||||||
MobileProduct mobileProduct = apiGateway.getProductMobile();
|
MobileProduct mobileProduct = apiGateway.getProductMobile();
|
||||||
|
|
||||||
assertEquals("20", mobileProduct.getPrice());
|
assertEquals(price, mobileProduct.getPrice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
package com.iluwatar.api.gateway;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Application test
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class AppTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test() throws Exception {
|
|
||||||
String[] args = {};
|
|
||||||
App.main(args);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user