Skip to content

Lab 13a: Setup for External API Call

Description

To support different currencies for the coffee order's total price, we'll call to an external currency conversion API using Spring's RestTemplate. This will be done in two steps:

  1. Create a stub service that returns a hard-coded value, to make sure it's integrated properly.

  2. A real, RestTemplate-based implementation that calls out to the API service.

Goals

  • Learn about using a Stub (type of Test Double) to ease development
  • Understand the basics of RestTemplate
  • See how @Profile can be used to switch among services used
  • Learn about handling HTTP query parameters with @RequestParam

a. Stub Service

You'll create a Stub version of the currency conversion service. Remember that a stub returns a hard-coded value.

  1. Create a new interface named CurrencyConversionService in the domain package with the following method:

    int convertToBritishPound(int amount);
    

    Note that this is a domain service, so has no annotations and is in the .domain package.

  2. Create a new package, com.welltestedlearning.coffeekiosk.adapter.out.currency and in that package, create a class named StubCurrencyConversionService that implements CurrencyConversionService and returns a hard-coded value of 1234.

  3. As this is an adapter service, add Spring's @Service annotation to the stub implementation.

b. Add Query Parameter

To support optionally returning the price in a different currency, we'll add support for a query parameter.

Open the CoffeeOrderController class and make the following changes:

  1. Use auto-wiring to inject the CurrencyConversionService interface as an additional parameter of the existing constructor and save it as a final instance field.

  2. Change the coffeeOrder() method to accept an optional Query Parameter by changing the method signature to:

    public ResponseEntity<CoffeeOrderResponse> coffeeOrder(
        @PathVariable("id") long orderId,
        @RequestParam(value = "currency", defaultValue = "usd") String currency) {
    

    The @RequestParam annotation will cause Spring to assign an incoming Query Parameter (e.g., ?currency=gbp) to the currency variable.

    The defaultValue="usd" means that if no query parameter is sent, currency will default to "usd".

c. Failing Test

Not all tests with Spring controllers have to use Spring support, we can write a unit test that instantiates the controller directly and test it as if Spring weren't involved.

  1. Create a new (or add to the existing) CoffeeOrderControllerTest class and add the following test:

    @Test
    public void getWithPoundQueryParamConvertsPriceToPounds() throws Exception {
      // Given: an order is in the repository
      CoffeeOrderRepository coffeeOrderRepository = new InMemoryCoffeeOrderRepository();
      // empty coffee order is fine as the price will be ignored for this test
      CoffeeOrder coffeeOrder = new CoffeeOrder("Spring", LocalDateTime.now());
      coffeeOrder.setId(12L); // need to have an id so we know we get an existing order
      coffeeOrderRepository.save(coffeeOrder);
    
      CoffeeOrderController controller =
          new CoffeeOrderController(coffeeOrderRepository, new StubCurrencyConversionService());
    
      // When: we do a GET with "gbp" currency
      CoffeeOrderResponse response = controller.coffeeOrder(coffeeOrder.getId(), "gbp").getBody();
    
      // Then: price will always be 1234, because that's what the Stub says
      assertThat(response.getTotalPrice())
          .isEqualTo("1234");
    }
    
  2. This test should fail, but all other tests should still pass.

d. Use the Service

In the CoffeeOrderController class, implement the following behavior in the coffeeInfo method:

  • If currency parameter is "gbp", then:

    1. Take the totalPrice from the coffee order (after it's retrieved from the repository)

    2. Use the CurrencyConversionService to convert the price into Pounds

    3. Store the converted price into the CoffeeOrderResponse as the price.

If implemented properly, the tests should pass.

e. Try It Out

Try it out using the browser, curl, or Postman, both with and without the query parameter of ?currency=gbp:

f. Increase Prices

To make the examples work better (since we're working with whole numbers), change the pricing in the CoffeeItem.price() method so that it's multiples of 100. Instead of 1, 2, and 3, change to 100, 200, and 300.