Pollito Dev
January 2, 2024

Contract-Driven Development 4: Generating controller interfaces

Posted on January 2, 2024  •  4 minutes  • 722 words  • Other languages:  Español

Using swagger codegen maven plugin to generate controller implementable code.

Check the github repo

This is a continuation of Contract-Driven Development 3: Creation of contracts .

Everything we’ll do here, you can find in in the github repo.

Spring City Explorer - Backend: Branch feature/cdd-4

Little fix to the OpenAPI.yaml files before moving on

In the OAS files, when we mention the elements of an enum, it is useful to enumerate each element between quotation marks.

In our current project this is important cause we have some enumerations that have the element NO in it, and without the quotation, the autogeneration of code will interpret NO as FALSE. This could cause some problems down the line.

Before:

enum: [published_desc, published_asc, popularity]

After:

enum: ["published_desc", "published_asc", "popularity"]

What are we looking to achieve?

The idea is that on build time, somehow someway magical but configurable, autogenerated code appears in the target folder of our project, that represents the behaviour we want our controllers to have.

diagram

Adding swagger codegen maven plugin to our codebase

For this, I’m gonna use the knowledge provided by Ammar Siddiqui in his github repo increment-service and his youtube video Swagger Codegen in 20 minutes!.

I won’t be following the video exactly step by step, but instead the main ideas he exposes.

Adding dependencies and plugin to pom.xml

Remember that you can check the final pom.xml file in the project github repo.

Here’s a brief description of the dependencies added:

Finally, the plugin that is gonna do the heavy lift, io.swagger.codegen.v3 (swagger-codegen-maven-plugin)

maven compile -> generated code!

Now everytime you do the “maven compile” task, you’ll see code generated in target/generated-sources/swagger/controllerinterfaces.

If you explore those .java files, you’ll immediatly notice that they are quite complex, with many annotations, comments, and obviously autogenerated stuff. And this is the main statement I want to leave living rent-free in your head after reading this blog:

The intricate processes outlined in the OpenAPI Specification ought to be automated, allowing developers to focus on their core business responsibilities.

Pollito, on his own blog

Creating controllers

Now let’s create the controllers inside a controller package in our src/main/java folder. For each controller:

It should look something like this:

Folder structure

folder structure

ArticleController

package dev.pollito.springcityexplorer.controller;

import dev.pollito.springcityexplorer.api.ArticleApi;
import dev.pollito.springcityexplorer.models.Articles;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ArticleController implements ArticleApi {
    @Override
    public ResponseEntity<Articles> getArticlesByCountry(String country, Integer limit, Integer offset) {
        return null;
    }
}

CommentController

package dev.pollito.springcityexplorer.controller;

import dev.pollito.springcityexplorer.api.CommentApi;
import dev.pollito.springcityexplorer.models.CommentPostBody;
import dev.pollito.springcityexplorer.models.Comments;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CommentController implements CommentApi {
    @Override
    public ResponseEntity<Comments> getComments(Integer limit, Integer offset) {
        return null;
    }

    @Override
    public ResponseEntity<Void> postComment(CommentPostBody body) {
        return null;
    }
}

WeatherController

package dev.pollito.springcityexplorer.controller;

import dev.pollito.springcityexplorer.api.WeatherApi;
import dev.pollito.springcityexplorer.models.Weather;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WeatherController implements WeatherApi {
    @Override
    public ResponseEntity<Weather> getWeatherByCity(String city) {
        return null;
    }
}

If everything went ok, now you’ll be able to see in the Spring -> MVC section of your IDE the endpoints generated by the implementation of the interfaces, with all the proper annotations (RequestMapping, RequestParam, NotNull, Valid, etc.). That’s some work that you saved yourself from doing.

endpoints

Running the current application

200 OK example

All current implementation are returning null, so there’s no body in the response, but we get OK status.

200 OK

400 Bad request example

Bad params

Next steps

Hey, check me out!

You can find me here