Have Beer – Will Swagger

Alright Swaggering onwards. In this blog post we’ll take a look at contract-first development with Swagger and Spring Boot. Again the code developed in this post will be available on GitHub. The Swagger api we’ve used is available on SwaggerHub.

Swaggerhub

There’s more than one way to skin a cat as it comes down to transforming a Swagger API into some workable code. This post will focus on using Swaggerhub to generate the code. For simplicity sake we’ll just use the Swagger api that was exposed in my previous blogpost by the springfox plugin (available at the /swagger-ui.html url). I’ve added the API to Swaggerhub and published it here.

Screenshot from 2018-01-28 14-56-03

Swaggerhub can generate server code from a Swagger API for a lot of different coding frameworks, where each framework on its turn has a plethora of options to tune its generated code.

Code generation

I like to put all generated code into a separate package to clearly distinguish it, from non-generated code. The latter can then be adjusted at will, while the former should only be adjusted by regeneration.

To tweak the settings for the Spring Boot application generation, click the settings icon (second icon on the right) in the top right corner of the Swaggerhub menu and select Edit CodeGen Options. Select Servers, Spring:

Screenshot from 2018-01-28 15-06-50

I’ve tweaked the following settings to get the code in my preferred package hierarchy:

configPackage nl.whitehorses.hellobeer.generated.config
delegatePattern enabled
apiPackage nl.whitehorses.hellobeer.generated.api
invokerPackage nl.whitehorses.hellobeer
modelPackage nl.whitehorses.hellobeer.generated.model

Some pointers here:

  • The delegate pattern – when enabled – enables you to keep the generated code seperated from the actual implementation code, meaning you don’t have to adjust any code available in the config, api and model packages to implement the api;
  • The api package will contain the HelloBeerApiDelegate interface (enabled by setting the aforementioned delegatePattern flag). This is the interface containing the methods we’re going to implement;
  • The model package will contain the Beer model class;
  • Make sure that the generated Spring Boot application (available in the invokerPackage) will be somewhere at the top of the package hierarchy as it will only scan the annotated Spring components deeper in the hierarchy;
  • There’s also an option called useBeanValidation. Flagging it didn’t seem to do much and it definitely didn’t have the desired effect. Omitting the required Beer.name property from a JSON POST request didn’t result in the desired validation error.

Now that the settings are set, we can put on our safety goggles and launch the code generator. Just click on the download icon (third icon on the right), select Server, Spring and you can download the zipfile with all the necessary code.

Screenshot from 2018-01-28 16-15-37

Implementation

The zipfile you downloaded in the previous step, contains a Maven pom file. Import it as a project in IntelliJ and you’re almost ready to go. You can give it your first swing, but on my first try I got an error upon booting. Somewhere in the error stack trace you’ll see the reason why:

java.lang.NoClassDefFoundError: javax/servlet/ServletContext

The solution is to get rid of the spring-boot-starter-tomcat dependency in the generated pom file. After doing that the application will boot.

Implementing the delegate

The model and repository code will be the same as the ones we used in our previous blogpost. The only thing we need to do, is implement the delegate interface. This is the code that will do the trick:

@Service
public class HelloBeerService implements HelloBeerApiDelegate {

    private static final Logger logger = LoggerFactory.getLogger(HelloBeerService.class);

    @Autowired
    private BeerConverter beerConverter;

    @Autowired
    private HelloBeerRepository helloBeerRepository;

    @Override
    public ResponseEntity addToBeerRepositoryUsingPOST(Beer beer) {
        logger.info("POST");

        nl.whitehorses.hellobeer.model.Beer beerEO = helloBeerRepository.save(beerConverter.fromDTOtoEO(beer));
        return ResponseEntity.ok(beerConverter.fromEoToDto(beerEO));
    }

    @Override
    public ResponseEntity<List> getAllBeersUsingGET(String type) {
        logger.info("GET");

        List beerDTOs;
List beerEOs;
        if (type == null) {
            beerEOs = helloBeerRepository.findAll();
        } else {
            beerEOs = helloBeerRepository.findByType(BeerType.valueOf(type));
        }

        beerDTOs = beerEOs.stream().map(beerConverter::fromEoToDto).collect(Collectors.toList());

        return ResponseEntity.ok(beerDTOs);
    }

}

I’ve implemented both methods (POST beer and GET beers) of the HelloBeerApiDelegate interface in the HelloBeerService service. Notice that most of the code reflects the code I’ve used in the previous blogpost.

I’ve had to put some code in place to convert the generated model Beer class (I labeled this one Data Transfer Object) to our Entity annotated model Beer class (the Entity Object) and vice versa though. I put this code in the BeerConverter class which is an annotated Spring Component. Through injection we’ll make the BeerConverter available to the Service.

Building the BeerConverter

@Component
public class BeerConverter {
    private static final Logger logger = LoggerFactory.getLogger(BeerConverter.class);

    public nl.whitehorses.hellobeer.generated.model.Beer fromEoToDto(Beer beerEO) {
        nl.whitehorses.hellobeer.generated.model.Beer beerDTO = new nl.whitehorses.hellobeer.generated.model.Beer();
        BeanUtils.copyProperties(beerEO, beerDTO);
        beerDTO.setType(nl.whitehorses.hellobeer.generated.model.Beer.TypeEnum.valueOf(beerEO.getType().toString()));

        return beerDTO;
    }

    public Beer fromDTOtoEO(nl.whitehorses.hellobeer.generated.model.Beer beerDTO) {
        Beer beerEO = new Beer();
        BeanUtils.copyProperties(beerDTO, beerEO);
        beerEO.setType(BeerType.valueOf(beerDTO.getType().toString()));

        return beerEO;
    }

}

The code speaks for itself. For the simple properties I just use the BeanUtils.copyProperties method that Spring provides. And for converting the beer type I put some custom code in place.

Unit testing the converter

To test if our converter works, I put the following unit test in place:

@RunWith(SpringRunner.class)
@SpringBootTest
public class BeerTest {

    @Autowired
    BeerConverter beerConverter;

    @Test
    public void fromEoToDto() {
        Beer beerEO = new Beer();
        beerEO.setId(1L);
        beerEO.setBrewery("Brand");
        beerEO.setName("Brand Saison");
        beerEO.setType(BeerType.OTHER);

        nl.whitehorses.hellobeer.generated.model.Beer beerDTO = beerConverter.fromEoToDto(beerEO);

        assertEquals("OTHER", beerDTO.getType().toString());

    }

    @Test
    public void fromDtoToEo() {
        nl.whitehorses.hellobeer.generated.model.Beer beerDTO = new nl.whitehorses.hellobeer.generated.model.Beer();
        beerDTO.setId(1L);
        beerDTO.setBrewery("Brand");
        beerDTO.setName("Brand Saison");
        beerDTO.setType(nl.whitehorses.hellobeer.generated.model.Beer.TypeEnum.OTHER);

        Beer beerEO = beerConverter.fromDTOtoEO(beerDTO);

        assertEquals("OTHER", beerEO.getType().toString());
    }

}

Look at how you can nicely inject the BeerConverter into the test class. The SpringBootTest annotation makes this possible.

Adding initial data

To get some initial data in our H2 database I’ve added the following data.sql file in the src/main/resources directory:

INSERT INTO beer (name, type, brewery) VALUES
  ('Brand Saison', 5, 'Brand');
INSERT INTO beer (name, type, brewery) VALUES
  ('Brand IPA', 3, 'Brand');

Testing

We can use the same urls we used in the previous blogpost for testing our new and improved contract-first api. So let us post a new beer first:

Screenshot from 2018-01-28 17-33-52

And finally, get all the beers. This will show our seeded data as well as the beer we POSTed in the previous step.

Screenshot from 2018-01-28 17-35-18

The nice thing about the code that Swaggerhub generated for us, is that they’ve also put the springfox functionality in, so our Swagger contract is automatically available at the /swagger-ui.html url:

Screenshot from 2018-01-28 17-37-33

Summary

That’s all for now folks! We’ve just shown how relatively easy it is to build and implement a Swagger API contract-first with the help of Swaggerhub and SpringBoot.

One of the downfalls of this approach is the need to transform the generated model to our own model classes. Luckily Spring can help us out a little here by using their BeanUtils class. The approach I advertise here is to build a Spring Component per model class in which you put the transform logic (one transform method from model (EO) to generated model (DTO) and one method from generated model to model). You can even define a nice generic interface for those classes. The Spring Component can then be injected into the service implementing the delegate interface for the REST controller.

As stated I couldn’t get the Bean Validation option to do its work in the generated code. I’ll play around some more in the near future and when I get it to work, will put a small additional blog post online.

Another downside is the manual step I had to perform to get the generated code into my maven project. In the next blog, I’ll have a go at automating this with the help of Maven.

References

Advertisements

Putting Spring Boot to REST with Swagger (and grab a beer in the process)

This blog post (the first in years) will be part of  a small series in which I’ll explore the world of Spring Boot (micro)services. I’ll start out by building a simple Spring Boot REST service and expose the service API via Swagger – the de facto standard for describing REST apis.

The code being built in the context of this blog post can be found on GitHub.

Hello Beer

Instead of building the obligatory HelloWorld service, I’ve decided to build a HelloBeer service instead, beer being one of the finer pleasures in life imho.

First Project

Let’s get to work! I’ll create the project with IntelliJ using the Spring Initializr and select the Web, JPA and H2 dependencies.

Screenshot from 2018-01-20 20-40-44

Screenshot from 2018-01-20 20-42-19

After clicking through to the finish line, you’ll get a working Spring Boot project with a pom file containing the dependencies you just selected, and the bootstrap class BeerAppApplication.

Model

The model is very simple: a small Beer class and a BeerType enum. I’ll put the JSR 303 annotations @NotNull on the name to demonstrate later on that the Swagger documentation can reflect this (by displaying the name as a required attribute of a Beer).

@Entity
public class Beer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @NotNull
    private String name;
    private BeerType type;
    private String brewery;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BeerType getType() {
        return type;
    }

    public void setType(BeerType type) {
        this.type = type;
    }

    public String getBrewery() {
        return brewery;
    }

    public void setBrewery(String brewery) {
        this.brewery = brewery;
    }
}

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

Repository

Adding a repository is pretty simple. Just extend the JpaRepository interface (the second typed parameter “Long” should correspond with the type of the id) and you get a lot of repository methods for free. For the fun of it, I’ve added an additional method for getting the list of beers based on the beer type.

public interface BeerRepository extends JpaRepository<Beer, Long> {

    List<Beer> findByType(BeerType beerType);
}

The interface (and the method we’ve added) will be magically implemented by Spring Boot.  And – because we’ve added H2 as a dependency – the beers we save, will be persisted to an H2 memory database (note: every time you restart, the database will be empty again) without writing any additional lines of code or configuration.

Controller

Now all we need to get this baby rolling, is a Restful controller. I’ve added a method for POSTing (saving) a beer and one for GETting a list of beers, optionally filtered by the beertype.

@RestController
@RequestMapping("/hello-beer/1.0")
public class BeerController {

    @Autowired
    private BeerRepository beerRepository;

    @RequestMapping(value = "/beer", method = RequestMethod.POST, produces = "application/json")
    public Beer addToBeerRepository(@RequestBody Beer beer) {

        return beerRepository.save(beer);
    }

    @RequestMapping(value = "/beers", method = RequestMethod.GET, produces = "application/json")
    public List<Beer> getAllBeers(@RequestParam(value = "type", required = false) String beerType) {
        if (beerType == null)
            return beerRepository.findAll();
        else
            return beerRepository.findByType(BeerType.valueOf(beerType));
    }

}

The code is pretty self-explanatory. Just wire in the repository and you’re ready to persist and retrieve some wonderful beers. The endpoints will be prepended with the path /hello-beer/1.0.

Testing

After running the service we can POST a few new beers on the URL /hello-beer/1.0/beer
Screenshot from 2018-01-20 21-53-33

Getting the list of beers filtered by type for example can be done like this: /hello-beer/1.0/beers?type=OTHER
Screenshot from 2018-01-20 21-53-58

Almost to easy, isn’t it? Now let’s try to document the API of our Hello Beer service using Swagger.

Swagger

To get the most out of our service we’ll be needing the following three dependencies:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.8.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.8.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-bean-validators</artifactId>
    <version>2.8.0</version>
</dependency>

The last dependency will interpret the JSR 303 annotations and put the in the API accordingly
Last piece of the puzzle is some configuration that needs to be added:

@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfig {
    @Bean
    public Docket api() {

        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("nl.whitehorses.beerapp.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    @Bean
    UiConfiguration uiConfig() {
        return UiConfigurationBuilder.builder().build();
    }

}

Noteworthy is the @Import line that’s needed for the JSR 303 implications to be transferred to the Swagger documentation.

Also take note of the RequestHandlerSelectors filter. I could have picked the RequestHandlerSelectors.any() filter, but if I did that, the error-controller would have ended up in my API adding some weird models in the process. This way I’ll keep my API nice and clean, just focussing on the beers.
Spinning up the application again, you can check the API on the url /swagger-ui.html:
Screenshot from 2018-01-20 22-11-04

As you can see by the red asterisk after the Beer.name property, the JSR 303 annotation is being picked up nicely by the swagger bean validator plugin.

By tweaking the configuration some more, you can probably get the Swagger API to look just the way you like it (for one we could change the title into something more meaningful than just Api Documentation by chaining the apiInfo method to the build method of the Docket).

Conclusion

In this post we’ve seen how easy it is to get a small Spring Boot REST service up and running and generate and expose the corresponding Swagger API documentation. The next blog post will see me exploring the world of contract first development.

I’ll then start by documenting the Swagger API and try to generate the necessary service interface code and only then start building the actual implementation.

References