Using a custom template for Swagger Codegen with Gradle

This article is the second part of a Swagger Codegen series. If you haven’t read the first one, make sure you do it before continuing. In this one I’ll show you how to use a customized template for code generation and what’s the problem with the standard template.

The problem

Let’s check out what we have in the server generated code with the base template.

@Api(value = "User", description = "the User API")
public interface UserApi {

    Logger log = LoggerFactory.getLogger(UserApi.class);

    default Optional<ObjectMapper> getObjectMapper() {
        return Optional.empty();
    }

    default Optional<HttpServletRequest> getRequest() {
        return Optional.empty();
    }

    default Optional<String> getAcceptHeader() {
        return getRequest().map(r -> r.getHeader("Accept"));
    }

    @ApiOperation(value = "Creates a new user", nickname = "createUser", notes = "", tags={ "user", })
    @ApiResponses(value = { 
        @ApiResponse(code = 201, message = "User successfully created"),
        @ApiResponse(code = 400, message = "Validation error") })
    @RequestMapping(value = "/user",
        produces = { "application/json" }, 
        consumes = { "application/json" },
        method = RequestMethod.POST)
    default ResponseEntity<Void> createUser(@ApiParam(value = "The user data" ,required=true )  @Valid @RequestBody UserCreateRequest user) {
        if(getObjectMapper().isPresent() && getAcceptHeader().isPresent()) {
        } else {
            log.warn("ObjectMapper or HttpServletRequest not configured in default UserApi interface so no example is generated");
        }
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
    }


    @ApiOperation(value = "Gets a list of users", nickname = "getUsers", notes = "", response = ListUserResponse.class, tags={ "user", })
    @ApiResponses(value = { 
        @ApiResponse(code = 200, message = "List of all users", response = ListUserResponse.class) })
    @RequestMapping(value = "/user",
        produces = { "application/json" }, 
        consumes = { "*/*" },
        method = RequestMethod.GET)
    default ResponseEntity<ListUserResponse> getUsers() {
        if(getObjectMapper().isPresent() && getAcceptHeader().isPresent()) {
            if (getAcceptHeader().get().contains("application/json")) {
                try {
                    return new ResponseEntity<>(getObjectMapper().get().readValue("\"\"", ListUserResponse.class), HttpStatus.NOT_IMPLEMENTED);
                } catch (IOException e) {
                    log.error("Couldn't serialize response for content type application/json", e);
                    return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
                }
            }
        } else {
            log.warn("ObjectMapper or HttpServletRequest not configured in default UserApi interface so no example is generated");
        }
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
    }

}

As you can see, there is a lot of default implementation for the methods which is not necessary for all the use-cases. getObjectMapper , getRequest , getAcceptHeader  and of course the implementations for the endpoint methods.

Overriding the template to be used

Using a custom template is not complicated at all. It’s just a configuration property for the generator class in the build.gradle. First of all, we have to download the currently used template by Swagger, you can do it by going to the GitHub repository of the code generator and find the templates there for the Spring language.

For the 2.3.1 code generator which we are using, you can find the templates here.

I’ve created a new folder in the contract project called template and copied there all the Spring template files from the Swagger Codegen. Now we have to set the generator to use this new template for code generation.

Let’s define a new property in the build.gradle  which will store the template directory.

ext.templateDir  = "${rootDir}/template"

Now we can use it for setting the templateDir  property of the CodegenConfigurator  class like the following.

config.setTemplateDir(rootProject.templateDir)          // The directory where the templates are located

After executing ./gradlew clean build install , everything should stay the same as it was before. We just integrated the copied templates into the code generation process, but we haven’t changed anything yet.

Changing the templates

Basically, the API interface we got after the generation is coming from the apiController.mustache  file. That’s the template for the UserApi  class. Mustache is a very basic templating language, I don’t want to go into details but you can find out more here.

We start by cleaning up the imports first as by default it has some conditional import statements in case of JDK8, BeanValidation library, Async execution, etc. and it has a @Generated  annotation as well which I usually don’t need.

The import looks like the following for me:

{{#imports}}import {{import}};
{{/imports}}
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.*;
import javax.validation.Valid;
import javax.servlet.http.HttpServletRequest;
import java.util.Optional;
import java.util.Optional;
import java.io.IOException;
import java.util.List;

Of course if you need less, remove it, if you need more, leave it there.

Now comes the Controller itself. Let’s remove the bunch of default methods and clear out the method for the operations. I like to use delegates for the APIs, so there is the API interface which defines the endpoints and there is a Controller class which has an implementation to call into a Delegate class which has the same methods. In this case it’s possible to add some default functionality into the Controller layer, like checking some user privileges, checking for localization, etc.

Now let’s focus on just clearing it up and we’ll jump back to adding a default functionality. The Controller template looks like this for me:

@RestController
{{#operations}}
public class {{classname}}Controller implements {{classname}} {
    private final {{classname}}Delegate delegate;

    @Autowired
    public {{classname}}Controller({{classname}}Delegate delegate) {
        this.delegate = delegate;
    }

{{#operation}}
    public ResponseEntity<{{>returnTypes}}> {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}}) {
        return delegate.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
    }

{{/operation}}
}
{{/operations}}

I switched the @Controller  annotation to be a @RestController  as I only use it for REST APIs and the Delegate will be wired into the Controller via constructor injection.

I also cleaned up the apiDelegate.mustache template which is obviously used for generating the Delegate interface. The final result looks the following:

package {{package}};

{{#imports}}import {{import}};
{{/imports}}
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Optional;

{{#operations}}
/**
 * A delegate to be called by the {@link {{classname}}Controller}}.
 * Implement this interface with a {@link org.springframework.stereotype.Service} annotated class.
 */
public interface {{classname}}Delegate {
{{#operation}}
    /**
     * @see {{classname}}#{{operationId}}
     */
    ResponseEntity<{{>returnTypes}}> {{operationId}}({{#allParams}}{{^isFile}}{{{dataType}}}{{/isFile}}{{#isFile}}MultipartFile{{/isFile}} {{paramName}}{{#hasMore}},{{/hasMore}}{{/allParams}});

{{/operation}}
}
{{/operations}}

Last but not least, cleaning up the API interface is necessary as well:

package {{package}};

{{#imports}}import {{import}};
{{/imports}}
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.Optional;
@Api(value = "{{{baseName}}}", description = "the {{{baseName}}} API")
{{#operations}}
public interface {{classname}} {
{{#operation}}

    @ApiOperation(value = "{{{summary}}}", nickname = "{{{operationId}}}", notes = "{{{notes}}}"{{#returnBaseType}}, response = {{{returnBaseType}}}.class{{/returnBaseType}}{{#returnContainer}}, responseContainer = "{{{returnContainer}}}"{{/returnContainer}}{{#hasAuthMethods}}, authorizations = {
        {{#authMethods}}@Authorization(value = "{{name}}"{{#isOAuth}}, scopes = {
            {{#scopes}}@AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{#hasMore}},
            {{/hasMore}}{{/scopes}}
            }{{/isOAuth}}){{#hasMore}},
        {{/hasMore}}{{/authMethods}}
    }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}",{{/vendorExtensions.x-tags}} })
    @ApiResponses(value = { {{#responses}}
        @ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{baseType}}}.class{{/baseType}}{{#containerType}}, responseContainer = "{{{containerType}}}"{{/containerType}}){{#hasMore}},{{/hasMore}}{{/responses}} })
    {{#implicitHeaders}}
    @ApiImplicitParams({
    {{#headerParams}}
    {{>implicitHeader}}
    {{/headerParams}}
    })
    {{/implicitHeaders}}
    @RequestMapping(value = "{{{path}}}",{{#singleContentTypes}}
        produces = "{{{vendorExtensions.x-accepts}}}",
        consumes = "{{{vendorExtensions.x-contentType}}}",{{/singleContentTypes}}{{^singleContentTypes}}{{#hasProduces}}
        produces = { {{#produces}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/produces}} }, {{/hasProduces}}{{#hasConsumes}}
        consumes = { {{#consumes}}"{{{mediaType}}}"{{#hasMore}}, {{/hasMore}}{{/consumes}} },{{/hasConsumes}}{{/singleContentTypes}}
        method = RequestMethod.{{httpMethod}})
    ResponseEntity<{{>returnTypes}}> {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}{{/allParams}});
{{/operation}}
}
{{/operations}}

Now as we have the templates cleaned up, let’s jump into the build.gradle  and enable the delegatePattern  property for the generation.

config.setAdditionalProperties([
    'dateLibrary'     : 'java8', // Date library to use
    'useTags'         : 'true',  // Use tags for the naming
    'delegatePattern' : 'true'   // Generating the code with the delegate pattern
])

Now after the changes we made, executing the code generation will result in the following classes (at least which are important for now):

  • UserApi  – this is the interface definition for the endpoints as before
  • UserApiController  – this is a basic controller class which implements the endpoints from the interface above and delegates the work to the UserApiDelegate  class
  • UserApiDelegate  – this one is the interface to implement the logic behind the endpoints and it’s called by the controller above

Now, with the new structure, implementing the UserApiDelegate  in the actual service application will result in the following code:

@Component
public class UserApiDelegateImpl implements UserApiDelegate {
    @Override
    public ResponseEntity<Void> createUser(UserCreateRequest user) {
        // TODO: implementation
        return null;
    }

    @Override
    public ResponseEntity<ListUserResponse> getUsers() {
        // TODO: implementation
        return null;
    }
}

As it’s not a controller anymore, we can simply mark it as a @Component  and it will be picked up by the generated controller.

Just a quick recap, in this part of the series, we saw how to use a custom template for code generation. It’s not a full customization as you can only change the existing templates, you cannot add new ones. Also, we’ve taken a quick look at how the delegate pattern looks. The full code can be found on GitHub.

Give it a try and give me some feedback how you feel about this, in the comments or on Twitter. In the next part we’ll explore more possibilities of the code generator.

7 Replies to “Using a custom template for Swagger Codegen with Gradle”

  1. kotto bass says:
    1. Arnold Galovics says:
  2. Ennio says:
    1. Saurabh Tyagi says:
    2. harsha says:
  3. Mice says:
    1. Arnold Galovics says:

Leave a Reply

Your email address will not be published. Required fields are marked *