#297 Create Spring Boot-backed API Gateway that aggregates calls to the Image and Price microservices

This commit is contained in:
tmcconville 2016-04-02 18:25:13 -05:00
parent 01737bc643
commit 665256ecc0
14 changed files with 208 additions and 73 deletions

View File

@ -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>

View File

@ -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;
} }
} }

View File

@ -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()));
} }
} }

View File

@ -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() {

View File

@ -0,0 +1,8 @@
package com.iluwatar.api.gateway;
/**
* An interface used to communicate with the Image microservice
*/
public interface ImageClient {
String getImagePath();
}

View File

@ -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;
}
}

View File

@ -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";
}
}

View File

@ -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() {

View File

@ -0,0 +1,8 @@
package com.iluwatar.api.gateway;
/**
* An interface used to communicate with the Price microservice
*/
public interface PriceClient {
String getPrice();
}

View File

@ -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;
}
}

View File

@ -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";
}
}

View File

@ -0,0 +1 @@
server.port=50004

View File

@ -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());
} }
} }

View File

@ -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);
}
}