JHipster – Streaming beer with Kafka and Spring Cloud Stream

Now that our OrderService is up and running, it’s time to make it a little more robust and decoupled. In this blog post we’re gonna put Kafka in between the OrderResource controller and our Spring Boot back-end system and use Spring Cloud Stream to ease development:

Blank Diagram

Upon creation of a JHipster application you will be given an option to select the Asynchronous messages using Apache Kafka option. After generation your pom file and application.yml will be all set up for using Kafka and Spring Cloud Stream. You’ll also get a docker file to spin up Kafka (and Zookeeper) and a MessageConfiguration class will be generated. There you need to declare your input and output channels (channels are Spring Cloud Stream abstractions, they’re the connection between the application and the message broker). If you follow the JHipster documentation on Kafka here – right after generating a virgin JHipster app – you should get a working flow up in no time.

Now, I wanna further improve upon the current HelloBeerTM application we finished in my previous blog post, and I didn’t check the Asynchronous messages option when I initially created the application. It’s not possible to add the option afterwards via the CLI, but luckily it’s not really that hard to add the necessary components manually. So let’s get started and makes those beer orders flow through a Kafka topic straight into our back-end application.
As always the finished code can be found on GitHub.

Kafka Docker image

Alright this guy I just ripped from a new JHipster app with the messaging option enabled. Add this kafka.yml file to the src/main/docker directory:

version: '2'
services:
    zookeeper:
        image: wurstmeister/zookeeper:3.4.6
        ports:
          - 2181:2181
    kafka:
        image: wurstmeister/kafka:1.0.0
        environment:
            KAFKA_ADVERTISED_HOST_NAME: localhost
            KAFKA_ADVERTISED_PORT: 9092
            KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
            KAFKA_CREATE_TOPICS: "topic-jhipster:1:1"
        ports:
            - 9092:9092

You can spin up Kafka now with this file by issuing the following command:

docker-compose -f src/main/docker/kafka.yml up -d

Adding the dependencies

The following dependencies are needed to enable Spring Cloud Stream and have it integrate with Kafka:

<!-- Kafka support -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream-test-support</artifactId>
  <scope>test</scope>
</dependency>

Configuring the channels

Since we only need one Kafka Topic, we can use the default channels that Spring Cloud Stream has to offer. We need one input and one output channel, so we can use the combined Processor interface. For a more complex setup with multiple topics, you can write your own custom interfaces for the channels (this is also the practice in the JHipster documentation example). For more information about channels check the Spring Cloud Stream Reference Guide.

MessagingConfiguration

First add the configuration for the Processor channel. This is done in the
MessagingConfiguration class. We’ll add this guy to the config package, the place where JHipster stores all Spring Boot configuration.

package nl.whitehorses.hellobeer.config;

import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Processor;

@EnableBinding(value = Processor.class)
public class MessagingConfiguration {
}

application-dev.yml

The overall configuration needs a few properties to let the application know where to find Kafka and to let Spring Cloud Stream channels bind to a Kafka topic. Let’s call the topic hb-orders. I’ll only put the configuration in the development configuration – application-dev.yml – for now:

spring:
    cloud:
        stream:
            kafka:
                binder:
                    brokers: localhost
                    zk-nodes: localhost
            bindings:
                output:
                    destination: hb-orders
                    content-type: application/json
                input:
                    destination: hb-orders

Note that input and output are the default channel names when working with the default channel interfaces.
That’s it for the channel configuration. Now we can use them in our back-end code.

OrderResource – Publishing to Kafka

Let’s alter our OrderResource so it publishes the OrderDTO object to the output channel instead of calling the OrderService directly:

@RestController
@RequestMapping("/api/order")
public class OrderResource {

  private static final String ENTITY_NAME = "order";
  private final Logger log = LoggerFactory.getLogger(OrderResource.class);
  private MessageChannel channel;

  public OrderResource(final Processor processor) {
    this.channel = processor.output();
  }

  @PostMapping("/process-order")
  @Timed
  public ResponseEntity processOrder(@Valid @RequestBody OrderDTO order) {
    log.debug("REST request to process Order : {}", order);
    if (order.getOrderId() == null) {
      throw new BadRequestAlertException("Error processing order", ENTITY_NAME, "orderfailure");
    }
    channel.send(MessageBuilder.withPayload(order).build());

    return ResponseEntity.ok(order);
  }
}

Not much going on here. Just inject the Processor and its channel and send the OrderDTO object through it.

OrderService – Subscribing to Kafka

@Service
@Transactional
public class OrderService {
  ....
  @StreamListener(Processor.INPUT)
  public void registerOrder(OrderDTO order) throws InvalidOrderException {
    ....
  }
  ....
}

Even simpler. The only change is adding the StreamListener annotation to the registerOrder method, making sure that guy sets off every time an order arrives at the topic.

Testing code

The spring-cloud-stream-test-support dependency (test-scoped) enables testing without a connected messaging system. Messages published to topics can be inspected via the MessageCollector class. I’ve rewritten the OrderResourceTest class to check if the OrderDTO is published to the message channel when calling the OrderResource:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = HelloBeerApp.class)
public class OrderResourceTest {

  @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
  @Autowired
  private Processor processor;

  @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
  @Autowired
  private MessageCollector messageCollector;

  private MockMvc restMockMvc;

  @Before
  public void setUp() {
    MockitoAnnotations.initMocks(this);

    OrderResource orderResource = new OrderResource(processor);
    restMockMvc = MockMvcBuilders
      .standaloneSetup(orderResource)
      .build();
  }

  @Test
  public void testProcessOrder() throws Exception {
    OrderItemDTO orderItem1 = new OrderItemDTO(1L, 50L);
    OrderItemDTO orderItem2 = new OrderItemDTO(2L, 50L);
    OrderDTO order = new OrderDTO();
    order.setCustomerId(1L);
    order.setOrderId(1L);
    order.setOrderItems(Arrays.asList(orderItem1, orderItem2));

    restMockMvc.perform(
      post("/api/order/process-order")
        .contentType(TestUtil.APPLICATION_JSON_UTF8)
        .content(TestUtil.convertObjectToJsonBytes(order)))
        .andExpect(status().isOk());

    Message<?> received = messageCollector.forChannel(processor.output()).poll();
    assertNotNull(received);
    assertEquals(received.getPayload(), order);

  }

}

In the OrderServiceIntTest I changed one of the test methods so it publishes an OrderDTO message on the (test) channel where the OrderService is subscribed to:

@Test
@Transactional
public void assertOrderOK() throws InvalidOrderException {
  ....
  //orderService.registerOrder(order);
  Message<OrderDTO> message = new GenericMessage<OrderDTO>(order);
  processor.input().send(message);
  ....
}

More information about Spring Cloud Stream testing can be found here.

Wiring it all up

Now let’s see if our beers will flow. So here are our stock levels before:
Screenshot-2018-5-23 Item Stock Levels

Now post a new (valid) order with Postman:
Screenshot from 2018-05-23 20-39-00

And behold our new stock levels:
Screenshot-2018-5-23 Item Stock Levels(1)

It still works! So our new setup with a Kafka topic in the middle is working like a charm! Note that this is a very simplistic example. To make it more robust – for one what about failed orders?! – the first step would be to move the topic consumer code away from the OrderService and put it in a separate class. That consumer class can delegate processing to an injected OrderService and deal with possible errors, eg. by moving the order to another topic. And with another topic you need custom interfaces for your channels as well.

Summary

In this blog post we introduced a Kafka topic to separate our Order clients from our Order processing. With the help of Spring Cloud Stream this is very easy to do. We also looked at a few ways to test messaging with Spring Cloud Stream.
The plan was to say goodbye to JHipster for now, but maybe I’ll do one more blog post. I wanna find out how easy it is to switch from Kafka to RabbitMQ or maybe improve upon this version and introduce a failed-order topic. I also wanna test how easy it is to upgrade this JHipster app to the latest version. So much ideas, so little time! Anyhow, let’s grab a beer first and think about that next blog!

References

Advertisements

JHipster – Adding some service

In our last blog post we focused on the Angular side of the generated application. This blog post is all about the Spring Boot server side part. In this post we’ll be adding some service to our HelloBeerTM app.
We’ll be developing on the app we’ve built in our previous JHipster blogs. Code can be found here.

But first let’s take a look at what’s in the server side part of our JHipster app.

Spring Boot architecture

05 - Spring boot architecture

All the entities we’ve added to our domain model will be exposed via REST operations. JHipster generates a layered architecture that corresponds to the hamburger in the picture.

The domain (or entity) object will be placed in the domain package. The corresponding repository will serve as the DAO and is placed in the repository package. Now if you’ve stuck to the defaults during generation, like I did, there will be no service and DTO layer for your entities (you can override this during generation). JHipster’s makers have the philosophy of omitting redundant layers. The service (and DTO) layers should be used for building complex (or composite) services that – for example – combine multiple repositories. The REST controllers by default just expose the domain objects directly and are placed in the web.rest package. JHipster calls them resources.

JHipster – Adding an OrderService

Eventually we wanna push our HelloBeer enterprise to the next level and start selling beers over the internet. So we need a REST service our customers can use for placing orders.

Order Service

So let us begin by adding an Order Service. The JHipster CLI has a command for this:

jhipster spring-service Order

Accept the defaults and in no time JHipster has generated the skeleton for our new OrderService:

package nl.whitehorses.hellobeer.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class OrderService {

    private final Logger log = LoggerFactory.getLogger(OrderService.class);

}

Order DTO

We also need an OrderDTO object to capture the JSON supplied to the exposed REST controller (which we’ll build in one of the next steps). Let’s keep it simple for now. Our OrderDTO contains an order reference (order id), a reference to our customer (customerId), and a list of inventory items (inventoryItemId and quantity) that the customer wants to order.

public class OrderDTO {
  private Long orderId;
  private Long customerId;
  private List<OrderItemDTO> orderItems;
  ....
}

public class OrderItemDTO {
  private Long inventoryItemId;
  private Long quantity;
  ..
}

Order Service implementation

For our OrderService implementation, we’re just gonna add a few checks making sure our inventory levels won’t run in the negative. If all checks are passed, our item stock levels are updated (i.e. new levels with new stock dates are inserted) according to the order.

@Service
@Transactional
public class OrderService {

  private final ItemStockLevelRepository itemStockLevelRepository;
  private final InventoryItemRepository inventoryItemRepository;

  private static final String ENTITY_NAME = "order";

  private final Logger log = LoggerFactory.getLogger(OrderService.class);

  public OrderService(ItemStockLevelRepository itemStockLevelRepository, InventoryItemRepository inventoryItemRepository) {
    this.itemStockLevelRepository = itemStockLevelRepository;
    this.inventoryItemRepository = inventoryItemRepository;
  }

  public void registerOrder(OrderDTO order) throws InvalidOrderException {
    // Map to store new item stock levels
    List<ItemStockLevel> itemStockLevelList = new ArrayList<>();

    for (OrderItemDTO orderItem : order.getOrderItems()) {
      ItemStockLevel itemStockLevelNew = processOrderItem(orderItem.getInventoryItemId(), orderItem.getQuantity());
      itemStockLevelList.add(itemStockLevelNew);
    }

    itemStockLevelRepository.save(itemStockLevelList);
    log.debug("Order processed");
  }

  // validate order items before processing
  // - assuming there are no multiple entries for one inventory item in the order
  // - if one order item entry fails, the whole order fails.
  private ItemStockLevel processOrderItem(Long inventoryItemId, Long qtyOrdered) {

    final InventoryItem inventoryItem = inventoryItemRepository.findOne(inventoryItemId);
    if (inventoryItem == null) {
      throw new InvalidOrderException("Invalid order", ENTITY_NAME, "invalidorder");
    }

    // find item stock level
    final Optional<ItemStockLevel> itemStockLevel = itemStockLevelRepository.findTopByInventoryItemOrderByStockDateDesc(inventoryItem);
    if (!itemStockLevel.isPresent()) {
      throw new InvalidOrderException("Invalid order", ENTITY_NAME, "invalidorder");
    }

    // check if quantity available
    Long qtyCurrent = itemStockLevel.get().getQuantity();
    Long newqty = qtyCurrent - qtyOrdered;
    if (newqty < 0L) {
      throw new InvalidOrderException("Invalid order", ENTITY_NAME, "invalidorder");
    }

    // construct new item stock level
    ItemStockLevel itemStockLevelNew = new ItemStockLevel();
    itemStockLevelNew.setInventoryItem(inventoryItem);
    itemStockLevelNew.setQuantity(newqty);
    itemStockLevelNew.setStockDate(ZonedDateTime.now(ZoneId.systemDefault()));
    return itemStockLevelNew;
  }

}

The code hopefully speaks for itself. In a nutshell: for every order item we first get the inventory item belonging to the inventory item id and check if it exists. Next we get the current item stock level for the inventory item. For this we’ve had to add the findTopByInventoryItemOrderByStockDateDesc method to the ItemStockLevelRepository first:

@SuppressWarnings("unused")
@Repository
public interface ItemStockLevelRepository extends JpaRepository<ItemStockLevel, Long> {

  Optional<ItemStockLevel> findTopByInventoryItemOrderByStockDateDesc(InventoryItem inventoryItem);
}

This gets us the item stock level at the most recent stock date (note that we get the implementation for free thanks to Spring). If such a level exists, we deduct the quantity ordered from the current quantity and if the current quantity is sufficient, we construct a new item stock level entry. After all order items are processed without validation errors, we store the new set of stock levels.
Not shown here are the OrderServiceIntTest in the nl.whitehorses.hellobeer.service package to test the new service and the new InvalidOrderException. Please check the GitHub code for the details.

Order Controller

Now let us add the controller for the Order Service. The JHipster CLI also has a command for this one:

jhipster spring-controller Order

Just add one POST action called processOrder, and see this controller (and a corresponding test class) being generated:

package nl.whitehorses.hellobeer.web.rest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Order controller
 */
@RestController
@RequestMapping("/api/order")
public class OrderResource {

  private final Logger log = LoggerFactory.getLogger(OrderResource.class);

  /**
  * POST processOrder
  */
  @PostMapping("/process-order")
  public String processOrder() {
    return "processOrder";
  }

}

Order Controller implementation

Alright. Find the code for the implementation below. Not much going on in here. The OrderService will be doing the heavy lifting and the controller just delegates to it.
We let the POST method return the supplied Order object after processing along with a status 200 code:

@RestController
@RequestMapping("/api/order")
public class OrderResource {

  private final Logger log = LoggerFactory.getLogger(OrderResource.class);

  private static final String ENTITY_NAME = "order";

  private final OrderService orderService;

  public OrderResource (final OrderService orderService) {
    this.orderService = orderService;
  }

  /**
  * POST processOrder
  */
  @PostMapping("/process-order")
  @Timed
  public ResponseEntity<OrderDTO> processOrder(@Valid @RequestBody OrderDTO order) {
    log.debug("REST request to process Order : {}", order);
    if (order.getOrderId() == null) {
      throw new InvalidOrderException("Invalid order", ENTITY_NAME, "invalidorder");
    }
    orderService.registerOrder(order);

    return ResponseEntity.ok(order);
  }

}

I’ve also implemented the generated test class OrderResourceTest. I’ve renamed it from OrderResourceIntTest. Since I’m integration testing the OrderService, I can suffice by unit testing the controller and just mocking the OrderService dependency. See the GitHub code for the test class implementation.

Beer tasting

All right. All pieces are in place to test our new service. If you fire up the application again and check the API menu (admin login required!), you’ll see the Swagger definition of our new service being displayed quite nicely:

Screenshot-2018-5-12 API

You can test the APIs by using the API page, but I prefer to user Postman for testing. You can get the urls from the Swagger definition. Please note that all APIs are secured and you need to send a header parameter along with the request. The parameter (called Authorization) can be found in the CURL section of the definition (click the Try it out! button first to see it):

Screenshot from 2018-05-12 20-34-59

Note that this parameter will change every time you reboot the server part of the application.

Now let’s first check our initial item stock levels:

Screenshot from 2018-05-12 20-45-04

As you can see, there is a stock level for inventory item 1 of 100 and a stock level for inventory item 2 of 100.

Let’s order 5 item 1 items and 10 item 2 items and see what happens!

Screenshot from 2018-05-12 20-54-44

Looking good so far: a response with a 200 Status Code containing the Order we put in our request.

Now for the final check: call the item stock level GET again:

Screenshot from 2018-05-12 20-56-23

And there you have it: two new item stock level entries with quantities of 95 for inventory item 1 and 90 for inventory item 2.

For completeness, I’ll also show you what happens when you try to order to much of one inventory item:

Screenshot from 2018-05-12 21-12-25

Great! So our validation also kicks in when needed.

JHipster – Contract-First

Now what about API-First development? As it turns out, JHipster supports that as well. If you paid attention during setup, you will have noticed the API-First option you can select. I didn’t select it when I generated the hb-jhipster application, but if I did, I would have seen a familiar entry in the generated Maven pom:

<plugin>
  <!--
    Plugin that provides API-first development using swagger-codegen to
    generate Spring-MVC endpoint stubs at compile time from a swagger definition file
  -->
  <groupId>io.swagger</groupId>
  <artifactId>swagger-codegen-maven-plugin</artifactId>
  <version>${swagger-codegen-maven-plugin.version}</version>
  <executions>
    <execution>
      <goals>
        <goal>generate</goal>
      </goals>
      <configuration>
        <inputSpec>${project.basedir}/src/main/resources/swagger/api.yml</inputSpec>
        <language>spring</language>
        <apiPackage>nl.whitehorses.hellobeer.web.api</apiPackage>
        <modelPackage>nl.whitehorses.hellobeer.web.api.model</modelPackage>
        <generateSupportingFiles>false</generateSupportingFiles>
        <configOptions>
          <interfaceOnly>true</interfaceOnly>
          <java8>true</java8>
        </configOptions>
      </configuration>
    </execution>
  </executions>
</plugin>

Yes that’s right: the swagger-codegen-maven-plugin. If you put your Swagger definition in a src/main/resources/swagger/api.yml file, you can generate the Spring Boot code according to the contract by running the following Maven command:

./mvnw generate-sources

See the JHipster documentation for more information.

Feel free to experiment, I’ve already done that in one of my previous blogs here.

Swagger codegenThe main difference between mine and the JHipster approach is that they’re using the interfaceOnly option, while I used the delegate option.
It’s a subtle difference and mainly a matter of taste. With the interfaceOnly option, you need to implement the API interface and build a controller class complete with annotations. With the delegateOnly option, the ApiController is generated for you and you only need to implement a simple interface. See the picture for the difference (the yellow objects are generated, the blue ones you need to implement yourself).

Summary

In this blog post we’ve explored the server layer of our JHipster app a bit. We used the CLI to generate a new Service and Controller for our HelloBeerTM application. It’s nice to see JHipster helping us as much as possible along the way.
Before exploring the glorious world of microservices and Spring Cloud, I’ll put out 1 last JHipster blog for now (I’ll probably come back to check out their microservice options). In that blog I’m gonna check out the Kafka integration option JHipster provides. So for now: happy drinking and stay tuned!

References

Tweaking the JHipster App – Show me you IDs please, ow wait, don’t!

Like every craft has to have a cool name, a flashy etiquette and needs to be poured into the right glass to please the hipster drinking it, the same applies to serving them a JHipster application: it’s all about presentation!
So let’s dig right into it and shave of some of ’em rough edges of our JHipster app. We’re building on the application we generated in the previous blog post. Code can be found here.

images

Presenting the relationships

Alright. One of the most annoying things that can happen when you’re in the middle of ordering a great craft on a warm Summer day, is some big dude demanding your ID right then and there and you discovering that you left the darn thing at home. So let us get rid of those IDs! Like the ones on the Beer page for example:
Screenshot-2018-4-2 Beers
Those brewery ids don’t mean squat to your average refined beer drinker, so let’s tackle them first. We wanna swap the displayed ids with the corresponding brewery names. But before diving into the code let us take a look at the generated components to see what we’re dealing with.

For every entity JHipster generates a folder into the webapp/app/entities folder. For the Beer entity, for example, we’ve got a beer subfolder. Within we find the beer.component.html that serves as our overview page. The beer-detail.component.html is what is displayed when you press View, the beer-dialog.component.html when you press Create or Edit and the beer-delete-dialog.component.html when you press Delete. They all have their corresponding TypeScript classes.

Screenshot from 2018-04-02 16-14-26

The beer.model.ts handles the model classes, the beer.service.ts and beer-popup.service.ts classes handle the REST calls to the lower Spring Boot layer, beer.route.ts tackles all routes regarding the Beer entity (think of menu items, bookmark urls, foreign key hyperlinks and the Create, View, Edit and Delete links). Everything is packed in a separate Angular module, i.e. beer.module.ts and index.ts just exports all typescript classes in the Beer entity folder upwards in the Angular hierarchy.

Alright. So for changing the overview page displaying our beers with brewery ids, the beer.component.html page is the guy we need. Since the relationship between Beer and Brewery is represented by the entire Brewery class (so not only by the Brewery id) in the Beer class, we have the name for the taking.

Let’s first change the BaseEntity interface (all entities in a relationship are derived from this one) and add an (optional) name to it. The BaseEntity interface is available in the src/main/webapp/shared/model folder.

export interface BaseEntity {
  // using type any to avoid methods complaining of invalid type
  id?: any;
  name?: any;
}

This change is mainly so the IDE won’t complain when we try to use the name property of a relationship somewhere in our html pages.

Now change the beer.component.html so it uses the name of the Brewery instead of the id. First change the table header (for sorting):

<th jhiSortBy="brewery.name">
  <span jhiTranslate="helloBeerApp.beer.brewery">Brewery</span>
  <span class="fa fa-sort"></span>
</th>

Now change the table body (for display):

<td>
  <div *ngIf="beer.brewery">
    <a [routerLink]="['../brewery', beer.brewery?.id ]" >{{beer.brewery?.name}}</a>
  </div>
</td>

Note that we didn’t change the link as it would break the navigation from Brewery to Brewery detail (those bold links are clickable) . That’s it. Refresh the Beer page and revel in the magic! Screenshot-2018-4-2 Beers(3) The view page (beer-detail.component.html) is even simpler, just replace the one line that is displaying the brewery:

<dd>
  <div *ngIf="beer.brewery">
    <a [routerLink]="['/brewery', beer.brewery?.id]">{{beer.brewery?.name}}</a>
  </div>
</dd>

And voilà the detail page is displaying brewery names now instead of useless ids: Screenshot-2018-4-2 Beers(4)

Creating and editing the relationships

The functionality for creating and editing entities are shared on the same html page (beer-dialog.component.html). We want to change the select item linking the beer to the brewery, so that it displays brewery names instead of ids: Screenshot-2018-4-2 Beers(1) This one couldn’t have been easier. Just head over to the div displaying the select item, keep the code that handles displaying/selecting the right relationship based on the brewery id and only change the displayed brewery id into the brewery name:

<div class="form-group">
  <label class="form-control-label" jhiTranslate="helloBeerApp.beer.brewery" for="field_brewery">Brewery</label>
  <select class="form-control" id="field_brewery" name="brewery" [(ngModel)]="beer.brewery" >
    <option [ngValue]="null"></option>
    <option [ngValue]="breweryOption.id === beer.brewery?.id ? beer.brewery : breweryOption" *ngFor="let breweryOption of breweries; trackBy: trackBreweryById">{{breweryOption.name}}</option>
  </select>
</div>

Check out the Edit page now: Screenshot-2018-4-2 Beers(5) See how the brewery name is being displayed instead of the id. How cool is that?!

Autosuggesting

Let’s take this one step further. The inventory item page is still displaying the id for the Beers: Screenshot-2018-4-2 Inventory Items We could change this, like we did in the previous steps and display a list of Beer names. But the list of Beer names could become huge, certainly bigger than the list of breweries. So what if we replaced this guy with an auto-complete item? Sounds great, doesn’t it?! But how do we do that? Enter PrimeNG. PrimeNG is a set of UI components for Angular applications.

Installation

First add the PrimeNG lib to your JHipster application

npm install primeng --save

Next, add the auto-complete component to the module where we’re gonna use it, i.e. inventory-item.module.ts:

...
import { AutoCompleteModule } from 'primeng/autocomplete';
...
@NgModule({
    imports: [
        AutoCompleteModule,
        ...
    ],
    ...
})

Typescript code

For this blog post, we’re just gonna filter the complete Beer list already retrieved by the REST call in the NgOnInit() method – another option would be to omit this initial retrieval and add a REST method that can handle a filter. Then, every time you make a change in the auto-complete item, an instant REST call is made retrieving a list based on the then present filter.

These are the changes needed for the inventory-item-dialog-component.ts:

export class InventoryItemDialogComponent implements OnInit {
...
  beers: Beer[];
  beerOptions: any[];
  ...
  search(event) {
    this.beerOptions = this.beers.filter((beer) => beer.name.startsWith(event.query));
  }
  ...
}

So basically we just add a method filtering the beers starting with the string matching our query. This method will be called every time the input in the auto-complete item changes and will update the selectable options accordingly.

HTML page

Now lets add the auto-complete item on the inventory-item-dialog.component.html page (overwrite the select item):

<p-autoComplete  id="field_beer" name="beer" [(ngModel)]="inventoryItem.beer"  [suggestions]="beerOptions" (completeMethod)="search($event)" field="name" placeholder="Beer"></p-autoComplete>

Check the PrimeNG manuals for more information. The most important piece is adding the field attribute so the auto-complete item can work with a Beer object.

Styling

When you test the JHipster app at this point, you’ll notice the auto-complete functionality actually working already, albeit that the styling looks horrible. Luckily you can get PrimeNG to play nicely along with JHipster’s styling – which is based on the popular Bootstrap CSS library. Just add a few lines to the vendor.css file:

@import '~bootstrap/dist/css/bootstrap.min.css';
@import '~font-awesome/css/font-awesome.css';
@import '~primeng/resources/primeng.css';
@import '~primeng/resources/themes/bootstrap/theme.css';

This is a major improvement. One last optimization is to expand the auto-complete item to a width of 100% just like all the other items on the dialog pages. Add these line to the global.css file:

.ui-autocomplete {
    width: 100%;
}
.ui-autocomplete-input {
    width: 100%;
}

Now, testing the inventory item Edit page you’ll see a nicely integrated auto-complete item: Screenshot-2018-4-11 Inventory Items In the overview and detail pages of the Inventory Item entity we’ll just make the same changes we made for the Beer pages, i.e. exchanging the displayed ids for names: Screenshot-2018-4-11 Inventory Items(1)

Calendar

Alright this beer’s on the house! As a small extra, we’ll add in a calendar item to beautify the item stock level page (and we’ll also change that ugly id). Screenshot-2018-4-16 Item Stock Levels As you can see the Stock Date field could use a good calendar to select the date time, and now we’re on it that ugly xml date presentation we could use without as well.

As we did for the auto-complete item, we’ll be using PrimeNG here again. PrimeNG supports a calendar item. It’s dependent on Angular’s animations module. So let’s first install that guy into our project:

npm install @angular/animations --save

And add the necessary imports to the item-stock-level.module.ts (the module where we’re gonna add the calendar item to).

...
import {CalendarModule} from 'primeng/calendar';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
...
@NgModule({
    imports: [
        CalendarModule,
        BrowserAnimationsModule,
        ...
    ],
    ...
})

Next add the PrimeNG calendar item itself (replace the input item representing the Stock Date) to the item-stock-level-dialog.component.html page:

<p-calendar id="field_stockDate" type="datetime-local" [showIcon]="true" name="stockDate" [(ngModel)]="itemStockLevel.stockDate" showTime="true" hourFormat="24" dateFormat="yy-mm-dd"></p-calendar>

Now, the date format used by JHipster isn’t compatible with this calendar item date format. So let’s change that. Format the stock date returned by the REST service in the item-stock-level-popup.service.ts to a format that the calender item understands, i.e. not the XML date format:

itemStockLevel.stockDate = this.datePipe
  .transform(itemStockLevel.stockDate, 'yyyy-MM-dd HH:mm');

And of course we also need to change the formatting of the stock date when we send it back to the back-end. Alter the item-stock-level.service.ts for this. Just comment out the formatting line (since where sending a plain javascript Date back):

/**
 * Convert a ItemStockLevel to a JSON which can be sent to the server.
 */
private convert(itemStockLevel: ItemStockLevel): ItemStockLevel {
    const copy: ItemStockLevel = Object.assign({}, itemStockLevel);

    // copy.stockDate = this.dateUtils.toDate(itemStockLevel.stockDate);
    return copy;
}

That’s it! Now look at the dialog page when editing an item stock level row. Looks pretty neat (I’ve also changed that id reference into an description reference (not visible in the picture), I’ll not explain it, you can look it up in the code):

Screenshot-2018-4-17 Item Stock Levels

Summary

In this fairly long blog post, we’ve tweaked the front-end of a generated JHipster application. We made quite a few changes to make the application a bit more presentable. We performed the following changes:

  • Changing relationships, replacing ids with meaningful strings;
  • Adding an auto-complete item;
  • Adding a calendar item.

For the last two step we used some PrimeNG components.

In the next blog post we’ll take a closer look at the server side of a JHipster application. So grab yourself a fine craft beer and stay tuned!

References

Getting the (J)Hipsters aboard

When I think of craft beers, I think of long beards and oiled mustaches. The biggest community of craft drinkers are definitely the hipsters and HelloBeerTM wants to add the contents of their shiny leather wallets to its bank account. So high time to get those hipsters on board and dive into JHipster.

JHipster is a generator tool able to generate web applications based on a Spring Boot back-end (exposed via REST) and an Angular front-end. In this first part of a series of blog posts – in which we’re gonna delve into JHipster to discover its nooks and crannies – we’ll generate our first (monolithic) JHipster application.

I’ll assume that you’ve got your JHipster development environment all set up (steps can be found here (choose Local installation with YARN)). I’m using the current latest version of JHipster, i.e. 4.14.1.

The GitHub code can be found here.

First contact

Let’s hit the ground running and generate the skeleton of our first JHipster application. We’ll put the project in the hb-jhipster directory and call it HelloBeer:

jhipster

The CLI (Command Line Interface) of JHipster will ask a lot of questions and demand answers to every one of them. Let’s choose the defaults for most of them, except for the package name (nl.whitehorses.hellobeer). We’ll also add support for an additional language (Dutch) besides English.

A couple of seconds after we answered the last question (10 seconds on my laptop), the application will be generated and can be started in two steps:

The back-end can be started with Maven by the following command:

./mvnw

The front-end can be fired up with the help of YARN:

yarn start

After both components have been started, the application greets us on localhost:9000:
Screenshot-2018-3-25 Welcome, Java Hipster
The application doesn’t have any business value yet, but it’s nice to see that with just a few steps we got a working application for free, with a homepage, login screen, user registration functionality, i18n and a most helpful Administration menu. That menu gives us insight into the inner workings of our JHipster app. It enables viewings of metrics, logs and it gives us a page displaying all the (Spring Boot) Swagger APIs available in the app (every REST service we’ll add to the application will be automatically added to this page):
Screenshot-2018-3-25 API

Domain model

Our beers aren’t exactly flowing yet. The  most important part is still missing: getting the domain model in place! There are two ways of doing this: JHipster UML and JDL Studio. I’ll use the latter, since it’s an online editor that’s very easy to use. Simply hop over to https://start.jhipster.tech/jdl-studio/ and you’re good to go!

We’ll use a slightly more complex domain model than the one we’ve used in my previous blog – which basically was just one domain object – albeit the most important one – Beer. In this blog I’ll use a few objects with relationships, just to get the most out of JHipster.

There will be a Beer object, a Brewery object, an InventoryItem object and an ItemStockLevel object. The domain model will allow us to keep track of our inventory of crafts – the types and stock – and will demonstrate how JHipster will generate its back- and front-end with multiple related tables involved.

Screenshot-2018-3-27 JDL-Studio

As you can see in the upper figure: you’ll get a nice diagram on the right for free when typing out the model on the left. This is the entire model in text form, which you get when you download it to your computer:

entity Beer {
  name String required
  type BeerType required
}

enum BeerType {
  LAGER, PILSNER, PALE_ALE, INDIA_PALE_ALE, PORTER, STOUT, OTHER
}

entity Brewery {
  name String required
  countryName String
}

entity InventoryItem {
  itemDescription String,
  serving ServingType required,
  amount Integer required,
  rating Integer
}

enum ServingType {
  CAN, BOTTLE
}

entity ItemStockLevel {
  stockDate ZonedDateTime required,
  quantity Long
}

relationship ManyToOne {
  Beer{brewery} to Brewery
}

relationship ManyToOne {
  InventoryItem{beer} to Beer
}

relationship ManyToOne {
  ItemStockLevel{inventoryItem} to InventoryItem
}

// Set pagination options
paginate Beer, InventoryItem with pagination
paginate Brewery with infinite-scroll

// dto * with mapstruct

// Set service options to all except few
// service all with serviceImpl

By default JHipster will generate Controller classes per Entity. These Controllers will delegate directly to the Repository classes exchanging the domain objects.
If you need more control you can uncomment the “dto * with mapstruct” and “service all with serviceImpl” lines. This will generate Service classes in between the Repository and Controller classes together with DTO classes. These Service classes will map the Entity objects (used by the Repository) to DTO objects (used by the Controller). So these DTO objects will be exposed to the front-end instead of the Entity objects, giving you a lesser coupled application.
For now lets stick to the architecture without the Service layer and leave those lines commented out.

jhipster import-jdl jhipster-jdl.jh

The CLI will ask you to override some files, choose yes for all of them (option a).

CRUD

When you fire up the application again, you’ll see that the Entities menu contains four additional entries (one for each domain object). Play round a bit. This is the Inventory Items page after entering some data:

Screenshot-2018-3-28 Inventory Items

And this is the Edit screen available when you click on the Edit button on the first row:

Screenshot-2018-3-28 Inventory Items(1)

As you can see, the required field are nicely validated (green when filled, red when empty), pagination is added and basically all the CRUD functionality is there. Pages are bookmarked as well. The above page eg. is available on the URL http://localhost:9000/#/inventory-item. Error and confirmation regions are displayed on the page when the data entered is invalid, resp. committed.

The biggest issue with the pages as-is after generation are those ugly ids you need to select to connect to a related domain object. Those same ids are displayed in the overview page. For example instead of the Beer Name (which is a Brand Pilsner) we’ll see a 1 displayed in the above screenshots.

Back-end

Don’t worry. There’s quite a few JHipster blog posts in the making and in those we will dissect quite a bit of all the objects that JHipster generates. And generate some more!
The thing I’d like to show you right now is how to test those Spring Boot REST services. We’ll need an endpoint and also a security header to test one of the services. Let’s check the Swagger page for the InventoryItem (login as an admin user). All the information to fire a GET request is available under the Try it Out button:

Screenshot from 2018-03-28 19-51-28

You need the Request URL and the Authorization header (the value unfortunately changes with every restart).  Here’s a test with Postman:

Screenshot from 2018-03-28 19-54-47

Front-end

The screens JHipster generates are functional albeit a bit crude. In the next blog post we’re gonna beautify ’em: getting rid of those ugly ids for one, use a calendar, add some auto-suggesting and who knows what else.

For now we’re gonna do some simple configuration, without changing any code: we’re gonna change the footer and beautify those list items a bit, so INDA_PALE_ALE will be displayed as Inda pale ale.

This is all part of the i18n functionality of JHipster. All the customized labels are available in JSON files in the src/main/webapp/i18n directories. Let’s change the beerType.json files (English and Dutch) :

Screenshot from 2018-03-28 20-08-34

The footer can be adjusted in the global.json file:

Screenshot from 2018-03-28 20-11-12

Now let’s fire up the application again, switch to the Dutch language and see the changes on the Beer page (check the footer and the Pilsner type):

Screenshot-2018-3-28 Beers(1)

Summary

That’s enough for now! In this first blogpost on JHipster we’ve seen how easy it is to get a basic CRUD application up and running. Most of the work goes into building the domain model and the rest of the heavy lifting is done by JHipster.

We’ve also done a little customization on the front-end. In our next blog post, we’re gonna look at how difficult or easy it is to spiff up the front-end a little more. So grab a beer and stay tuned!

References

HelloBeer goes Angular – Finale

Alright guys. This is the last and final episode of our – soon to be released on Netflix – blockbuster series of HelloBeerTM goes Angular starring lots of frustrated beer drinking developers. In this grande finale we’re gonna finish the app we developed in our previous blog posts here and here and add some much needed error handling, css styling and i18n.
The final code can be found here on Github.

Bootstrap CSS

I was using a bootstrap.css file that I snatched somewhere from the internet when I started developing the app, but I recently found out you can just add it as a dependency module. To install the bootstrap version I’ve been using you can add it to the project like this:

npm install bootstrap@3.3.2 --save

Now you can add the import to the styles.css file like this

@import '../node_modules/bootstrap/dist/css/bootstrap.css';

Error handling

The POST method of our REST service expects a beer name to be present in the JSON request. When you leave it blank the beer won’t be added to the inventory, the server will return an error and the JSON error message will be logged to the console. Let’s change that a bit and make it a more in-your-face-experience for the user.

First change the edit-beer.component.ts file to store a possible error in a model variable:

import {Component, EventEmitter, OnInit, Output} from '@angular/core';

import {ApiClientService} from "../services/beer";
import {Beer, Type} from '../services/beer/models';

@Component({
  selector: 'app-edit-beer',
  templateUrl: './edit-beer.component.html',
  styleUrls: ['./edit-beer.component.css']
})
export class EditBeerComponent implements OnInit {

  @Output() onBeerPosted = new EventEmitter<Beer>();

  public beer: Beer = {} as Beer;
  public beerTypes: string[];

  public error: string;

  constructor(private apiClientService: ApiClientService) {
    this.beerTypes = (Object.keys(Type));
  }

  /**
   * Call REST service to POST a beer
   */
  public postBeer(): void {
    this.apiClientService.addToBeerRepositoryUsingPOST(this.beer)
      .subscribe(resp => {
        this.pushBeer(resp.body)
      }, (error => {
        console.log(error);
        this.onError(error.error.message)
      }));
  }

  private pushBeer(beer: Beer): void {
    // add beer to app
    this.onBeerPosted.emit(beer);
    this.reset();
  }

  private onError(message: string): void {
    this.error = message;
  }

  private reset(): void {
    this.beer = {} as Beer;
    this.error = null;
  }

  ngOnInit() {
  }

}

And add the following html snippet at the top of our edit-beer.component.html page (just below the title).

<div [hidden]="!error" class="panel panel-danger panel-body">
  <span class="text-danger">{{error}}</span>
</div>

This will only show the div when there’s actually an error message there.
Now when we try to post a nameless beer to our inventory, we’ll see the error appearing on our screen, like this:
Screenshot-2018-3-5 HbAngularClient
It’s not the most helpful error message, but I’ll leave it up to the reader to pluck a better error message out of the quite verbose REST response.

i18n

Finally, let’s get rid of those ugly beer types. Again this took me a while to get it right, but in the end I found the module that got the job done. It’s called ngx-translate and I basically followed the steps explained on this how-to here.

First add the modules to the app:

npm install @ngx-translate/core --save
npm install @ngx-translate/http-loader --save

Alter the app.module.ts file (consult the how-to page):

import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {FormsModule} from "@angular/forms";
import {HttpClient, HttpClientModule} from '@angular/common/http';
// import ngx-translate and the http loader
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {TranslateHttpLoader} from '@ngx-translate/http-loader';

import {AppComponent} from './app.component';
import {EditBeerComponent} from './edit-beer/edit-beer.component';

import {ApiClientService} from './services/beer/index';

@NgModule({
  declarations: [
    AppComponent,
    EditBeerComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient]
      }
    })
  ],
  providers: [ApiClientService],
  bootstrap: [AppComponent]
})
export class AppModule {
}

// required for AOT compilation
export function HttpLoaderFactory(http: HttpClient) {
  return new TranslateHttpLoader(http);
}

Next, inject the TranslateService in the app component:

....
import {TranslateService} from "@ngx-translate/core";
....
  constructor(private apiClientService: ApiClientService, private translateService: TranslateService) {
    translateService.setDefaultLang('en');
  }
....

Add the en.json file with the beer type translations to the assets/i18n directory:

{
  "LAGER": "Lager",
  "PILSNER": "Pilsner",
  "PALE_ALE": "Pale Ale",
  "INDIA_PALE_ALE": "India Pale Ale",
  "STOUT": "Stout",
  "OTHER": "Other"
}

Now you can add the translate pipes to the fields displaying the beer types (there’s one on the app.component.html and one on the edit-beer.component.html), like this:

{{beer.type | translate}}

Now test the application. If you’ve made no mistake it should look like this now (check the beer types, they’re nicely translated):
Screenshot-2018-3-5 HbAngularClient(1)

Summary

In these last three blog posts we’ve seen how relatively easy it is to build a small Angular app consuming a Swagger API. The typescript code for consumption was generated by the angular-swagger-client-generator module.

The combination of a Spring Boot back end and an Angular front end is quite popular. As a matter of fact there’s an open source generator out there built around these two frameworks, one that’s gaining quite a lot of traction lately.

In my next blog post I’m gonna market my HelloBeerTM beers to one of the largest groups of craft beer drinkers out there: the hipsters! You guessed it, time to check out JHipster!

References

HelloBeer goes Angular – part II

Alright, let’s keep going. Our Angular clients are getting thirsty! In our previous blog post we’ve built the first part of our Angular app consuming a REST service based on a Swagger API. Let’s finish the app.

Consuming the POST method

To POST beers we’re gonna add a new Form component to our Angular app. We could have put the code in the main app component, but it’s always a good idea to keep your components small. So, to create the new component – we’ll call it edit-beer – just hit:

ng generate component edit-beer

First we’ll fill in the edit-beer.component.ts component code. We need a beer model variable, a list of beerTypes to serve as a drop-down list and a method for posting a beer. Here you go:

import {Component, OnInit} from '@angular/core';

import {ApiClientService} from "../services/beer";
import {Beer, Type} from '../services/beer/models';

@Component({
  selector: 'app-edit-beer',
  templateUrl: './edit-beer.component.html',
  styleUrls: ['./edit-beer.component.css']
})
export class EditBeerComponent implements OnInit {

  public beer: Beer = {} as Beer;
  public beerTypes: string[];

  constructor(private apiClientService: ApiClientService) {
    this.beerTypes = (Object.keys(Type));
  }

  /**
   * Call REST service to POST a new beer
   */
  public postBeer(): void {
    this.apiClientService.addToBeerRepositoryUsingPOST(this.beer)
      .subscribe(resp => {
        this.reset();
      }, (error => {
        console.log(error);
      }));
  }

  private reset(): void {
    this.beer = {} as Beer;
  }

  ngOnInit() {
  }

}

The first time I was testing with the generated code and used the Type interface in my typescript code, I got these messages from IntelliJ:

Screenshot from 2018-02-20 21-09-10

Apparently I was using an older version of typescript, that couldn’t handle string enums. Updating to the newest version of typescript fixed this issue.

Let’s put the form on the html component – edit-beer.component.html – that will enable us to post new beers to our inventory:

<div>
  <div class="well lead">Add a beer</div>
  <form method="POST" class="form-horizontal">
    <div class="row">
      <div class="form-group col-md-12">
        <label class="col-md-3 control-lable" for="input.name">Name</label>
        <div class="col-md-7">
          <input [(ngModel)]="beer.name" type="text" id="input.name" name="name" class="form-control input-sm"/>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="form-group col-md-12">
        <label class="col-md-3 control-lable" for="input.type">Beer type</label>
        <div class="col-md-7">
          <select [(ngModel)]="beer.type" id="input.type" name="type" class="form-control input-sm">
            <option *ngFor="let beerType of beerTypes" [value]="beerType">{{beerType}}</option>
          </select>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="form-group col-md-12">
        <label class="col-md-3 control-lable" for="input.brewery">Brewery</label>
        <div class="col-md-7">
          <input [(ngModel)]="beer.brewery" type="text" id="input.brewery" name="brewery" class="form-control input-sm"/>
        </div>
      </div>
    </div>
    <div>
      <input (click)="postBeer()" title="OK" class="btn btn-primary custom-width float-right"/>
    </div>
  </form>
</div>

Note the postBeer() method coupled to the button click event binding.

Add the edit-beer component at the bottom of the app component and fire up the app to see my next rookie mistake appearing in the console:

Can't bind to 'ngModel' since it isn't a known property of 'input'.

This took me while to figure out. The error – though a bit vague – is all about forgetting to import the FormsModule in the app module, so let’s fix that:

import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {FormsModule} from "@angular/forms";
import {HttpClientModule} from '@angular/common/http';

import {AppComponent} from './app.component';
import {EditBeerComponent} from './edit-beer/edit-beer.component';

import {ApiClientService} from './services/beer/index';

@NgModule({
  declarations: [
    AppComponent,
    EditBeerComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule
  ],
  providers: [ApiClientService],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Now after running the app again, we’ll see a familiar screen appearing:
Screenshot-2018-3-4 HbAngularClient

You can enter a new beer and after hitting the OK button, the beer is posted. When you refresh the screen (don’t worry we’re gonna fix that), you can see that the list is updated with the beers you just added.

Updating the beer inventory instantly

Hitting refresh to update the beer inventory after adding a new beer every time, is bound to piss some clients off, especially when one of ’em is an angry drunk. So let’s fix that asap!

Now how do we do that? We obviously need a way to communicate between the edit-beer component and the app component: events to the rescue!

The application will require a few changes. Let start with the edit-component that will fire the event. This is the new code:

import {Component, EventEmitter, OnInit, Output} from '@angular/core';

import {ApiClientService} from "../services/beer";
import {Beer, Type} from '../services/beer/models';

@Component({
  selector: 'app-edit-beer',
  templateUrl: './edit-beer.component.html',
  styleUrls: ['./edit-beer.component.css']
})
export class EditBeerComponent implements OnInit {

  @Output() onBeerPosted = new EventEmitter<Beer>();

  public beer: Beer = {} as Beer;
  public beerTypes: string[];

  constructor(private apiClientService: ApiClientService) {
    this.beerTypes = (Object.keys(Type));
  }

  /**
   * Call REST service to POST a new beer
   */
  public postBeer(): void {
    this.apiClientService.addToBeerRepositoryUsingPOST(this.beer)
      .subscribe(resp => {
        this.pushBeer(resp.body)
      }, (error => {
        console.log(error);
      }));
  }

  private pushBeer(beer: Beer): void {
    // add beer to app
    this.onBeerPosted.emit(beer);
    this.reset();
  }

  private reset(): void {
    this.beer = {} as Beer;
  }

  ngOnInit() {
  }

}

The REST service will return a beer after it’s been posted. So we’ll gonna send that new beer on the event to the app component. That guy will use it to update its beers list.

The event will be emitted from the edit-beer.component, so reflect that in the app.component.html, here you’ll glue the methods from the app and edit-beer components together:

<app-edit-beer (onBeerPosted)="onBeerPosted($event)"></app-edit-beer>

Last piece of the puzzle is catching the event in the app component. Just add this code snippet to the app.component.ts and you’re done.

public onNewBeer(beer: Beer): void {
  this.beers.push(beer);
}

Test the app again and see the magic happening when you add a new beer. Flashy is it not?

Summary

We’re almost there. I was planning to finish this blog in 2 parts, but like with every great blockbuster movie of the 80s, we need a third installment.

In the last part of our HelloBeer goes Angular trilogy we’ll wrap it all up with some user-friendly error handling. We’ll also add i18n to our app to improve upon those ugly beer types. And then we’re really done!

HelloBeer goes Angular – Part I

You can never have enough clients for your beers, right? In our last post we served our Spring server-side clients mostly interested in Java flavored beers. Lets see how difficult it is to generate stub code for our Swagger API geared towards a more client-side oriented beer drinker. In this post we’ll generate some Typescript stubs on which we’ll try to build an Angular front-end application. Now, I’m not an expert in Angular so I obviously ran into a couple of rookie mistakes. In this blogpost I’m gonna show you the way I build the application and I’ll also point out some of the pitfalls I ran into.

Needless to say but the code will be available on GitHub. Since this blogpost is gonna be pretty big, I’ve decided to split it up into two parts. In this first part we’re gonna generate the stub and consume the GET part of the REST service. The link to the final Github code will be posted in part II.

New project

So let’s start brewing. First things first. For starters create our new project and call it hb-angular-client

ng new hb-angular-client

You can fire up your first incarnation by hitting “ng serve”, check http://localhost:4200 and at this time you should see a working application. Next, we’re gonna recreate the front-end we build with Spring MVC in our previous blogpost and make our Angular app look similar. The final version will look like this:

Screenshot-2018-3-3 HelloRoger

Generating the Typescript stub

This is probably the most important part. The rest is just building Angular. To generate the stub code for our Swagger API, I’m gonna use the angular4-swagger-client-generator. Since I’ve got a feeling I’m gonna use this generator more than just for this project, I’ll install it globally (hence the -g):

npm install -g angular4-swagger-client-generator

Now let’s generate our stub code. Make sure the output directory already exists. For our source we’re pointing at our Swagger api available on SwaggerHub. Note that opposed to the Maven generation in our previous blogpost where we took the yaml file as the source, we’re now pointing at the json file:

a4apigen -u https://app.swaggerhub.com/apiproxy/schema/file/rphgoossens/hello-beer/1.0/swagger.json -o ./src/app/services/beer

The generator will create a model file containing the Beer and Type interfaces and the index.ts file that will contain the methods with which we can communicate with our REST service:

  /**
  * Method addToBeerRepositoryUsingPOST
  * @param beer beer
  * @return Full HTTP response as Observable
  */
  public addToBeerRepositoryUsingPOST(beer: Beer): Observable {
    let uri = '/hello-beer/1.0/beer';
    let headers = new HttpHeaders();
    let params = new HttpParams();
    return this.sendRequest('post', uri, headers, params, JSON.stringify(beer));
  }

  /**
  * Method getAllBeersUsingGET
  * @param type type
  * @return Full HTTP response as Observable
  */
  public getAllBeersUsingGET(type: string): Observable {
    let uri = '/hello-beer/1.0/beers';
    let headers = new HttpHeaders();
    let params = new HttpParams();
    if (type !== undefined && type !== null) {
      params = params.set('type', type + '');
    }
    return this.sendRequest('get', uri, headers, params, null);
  }

Consuming the GET method

Let us first build the top part of the app, i.e. the table that will show us the beers currently served by the REST service. After putting the style sheets in place, we need to build the app.component.html and the app.component.ts code.
The typescript file will contain a call to the generated REST service stub. To inject the generated service in an Angular component, we first need to declare it as a Provider. Since we’ll be needing this service throughout the entire application, let’s declare it at the module level. Alter the app.module.ts like this:

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {HttpClientModule} from '@angular/common/http';

import {AppComponent} from './app.component';

import {ApiClientService} from './services/beer/index';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [ApiClientService],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Note that the REST service is dependent on the HttpClientModule. I’ve added that one as an import as well.

Next, let’s build the component code. We need to import the model and service classes, inject the service and call it from the ngOnInit method to populate the beers array that will be exposed via our html page on startup.
This is the app.component.ts code so far. The important parts are highlighted.

import { Component, OnInit } from '@angular/core';

import {ApiClientService} from './services/beer/index';
import {Beer} from './services/beer/models';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  public beers: Beer[];

  constructor(private apiClientService: ApiClientService) {
  }

  ngOnInit() {
    this.apiClientService.getAllBeersUsingGET(null)
      .subscribe(resp => {
        this.beers = resp.body;
      }, error => {
        console.log(error);
      });
  }
}

Last piece of the puzzle is the app.component.html page. It’s pretty straightforward. The most important part is the part for exposing the beers

<tr *ngFor="let beer of beers">
  <td><span>{{beer.name}}</span></td>
  <td><span>{{beer.type}}</span></td>
  <td><span>{{beer.brewery}}</span></td>
  <td>
    <a href="" class="btn btn-success custom-width">edit</a>
  </td>
  <td>
    <a href="" class="btn btn-danger custom-width">delete</a>
  </td>
</tr>

Taking a look at the app, we can see that our beers are being displayed quite nicely (of course after our hello-beer-server Spring Boot REST service has been spun up):

Screenshot-2018-3-3 HbAngularClient

CORS

One of the pitfalls – one that every REST developer will run into at least once in his lifetime – is the infamous Cross-Origin Request Block error. In my first attempt the GET request data wasn’t showing in my application.

Checking the Firefox Developer Tools (Network Tab) I could see that the REST service was being called, responding with a couple of Beers.

The console was showing me this error though:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/hello-beer/1.0/beers. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

Basically what this boils down to, is that you can’t make a service request from a localhost app when that service is also running on localhost albeit on a different port. The browser simply will not accept the response.

The easiest solution is to change the REST service to let it generate responses that the browser will accept. So that’s what I did. I added the following configuration to my Spring Boot Rest Service:

@Configuration
public class HelloBeerConfiguration {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**");
            }
        };
    }
}

Summary

In this first part of a blog post series of 2 we’ve generated a Typescript client for our Swagger API and build a small Angular app that consumes the REST service implementing the Swagger API, on top of it. So far we’ve only consumed the GET method of the REST service.

In our 2nd part we’re gonna consume the POST part, add some error handling and as a bonus some i18n and finally we’re gonna wrap it all up. So stay tuned!!

References