This is a Maven plugin designed to help developers automatizing the creation of code classes from YML files based on AsyncApi and OpenAPI.

Overview

Codacy Badge Maven Central

SCS MultiApi Maven Plugin

This is a Maven plugin designed to help developers automatizing the creation of code classes from YML files based on AsyncApi and OpenAPI.

Index

Main Configuration

This plugin allows developers to automatize the creation of code classes for REST and Kafka connections, based on YML files under the AsyncApi and OpenApi specifications. In the latter case, many of the configuration options and classes that are generated are based on reimplementation or modification of the OpenAPI Generator models and template designs.

The generation of the REST and Kafka connections is independent each other and could be used only one, or both at the same time.

Here is the documentation for these technologies:

How to configure the POM file

To mantain the generation of the diferent types of classes independent, they are configured as two different goals on the plugin, asyncapi-generation and openapi-generation. As commented above, they both could be used at the same time, setting a double execution for the plugin in the pom.xml file.

<plugin>
  <groupId>net.coru</groupId>
  <artifactId>scs-multiapi-maven-plugin</artifactId>
  <version>3.0.3</version>
  <executions>
    <execution>
      <id>asyncapi</id>
      <phase>generate-sources</phase>
      <goals>
        <goal>asyncapi-generation</goal>
      </goals>
      <configuration>
        <fileSpecs>
          ...
        </fileSpecs>
      </configuration>
    </execution>

    <execution>
      <id>openapi</id>
      <phase>generate-sources</phase>
      <goals>
        <goal>openapi-generation</goal>
      </goals>
      <configuration>
        <fileSpecs>
          <fileSpec>
            ...
          </fileSpec>
        </fileSpecs>
      </configuration>
    </execution>
  </executions>
</plugin>

In the example above, you can see a partial configuration for the plugin with a double execution. This makes neccesary to set an id for each execution, asyncapi and openapi in this case.

In the case that you only want to run one of the goals of the plugin, you only need to remove the execution section that you don't need.

In the AsyncApi Generator and the OpenApi Generator sections, you can find more information about how they work, and the parameters and configuration options they offer.

AsyncApi Generator

Configuration

The plugin defined phase and goal parameters are expected to be generate-sources and asyncapi-generation, as they are the only values for which the plugin is designed.

<plugin>
<groupId>net.coru</groupId>
<artifactId>scs-multiapi-maven-plugin</artifactId>
<version>3.0.3</version>
<executions>
  <execution>
    <phase>generate-sources</phase>
    <goals>
      <goal>asyncapi-generation</goal>
    </goals>
    <configuration>
      <fileSpecs>
        <fileSpec>
          <filePath>PATH_TO_YML</filePath>
        </fileSpec>
        <fileSpec>
          <filePath>PATH_TO_YML</filePath>
          <consumer>
            <ids>publishOperation</ids>
            <classNamePostfix>MY_CONSUMER_CLASS</classNamePostfix>
            <modelNameSuffix>DTO</modelNameSuffix>
            <apiPackage>net.coru.apigenerator.asyncapi.business_model.model.event.consumer</apiPackage>
            <modelPackage>net.coru.apigenerator.asyncapi.business_model.model.event</modelPackage>
          </consumer>
          <supplier>
            <ids>subscribeOperation</ids>
            <apiPackage>net.coru.apigenerator.asyncapi.business_model.model.event.producer</apiPackage>
            <modelPackage>net.coru.apigenerator.asyncapi.business_model.model.event</modelPackage>
          </supplier>
        </fileSpec>
      </fileSpecs>
      <generatedSourcesFolder>sources-generated</generatedSourcesFolder>
    </configuration>
  </execution>
</executions>
</plugin>

As you can see in the example above, there is a main parameter fileSpecs that receives a list of fileSpec attributes groups, so you can set as many YML files as you want.

fileSpecs could be configured in two different ways:

  1. The first one is to configure only the YML file. This is made using the filePath parameter, that expects to receive the path to the file. Using the plugin in this way, you can't configure the model package or the api package in the pom file, neither other options, so they will be configured as its explained in apiPackage and modelPackage sections.
    This way it's limited to the usage of Consumer and Supplier methods.
<fileSpec>
    <filePath>PATH_TO_YML</filePath>
</fileSpec>
  1. The second one is to configure the YML file with the consumers, supplier producers and streamBrige producers that you want to generate.
<fileSpec>
    <filePath>PATH_TO_YML</filePath>
    <consumer>
        <ids>publishOperation</ids>
        <classNamePostfix>MY_CONSUMER_CLASS</classNamePostfix>
        <modelNameSuffix>DTO</modelNameSuffix>
        <apiPackage>net.coru.apigenerator.asyncapi.business_model.model.event.consumer</apiPackage>
        <modelPackage>net.coru.apigenerator.asyncapi.business_model.model.event</modelPackage>
    </consumer>
    <supplier>
        <ids>subscribeOperation</ids>
        <apiPackage>net.coru.apigenerator.asyncapi.business_model.model.event.producer</apiPackage>
        <modelPackage>net.coru.apigenerator.asyncapi.business_model.model.event</modelPackage>
    </supplier>
    <streamBridge>
        <ids>streamBridgeOperation</ids>
        <apiPackage>net.coru.apigenerator.asyncapi.business_model.model.event.producer</apiPackage>
        <modelPackage>net.coru.apigenerator.asyncapi.business_model.model.event</modelPackage>
    </streamBridge>
</fileSpec>

As you can see in the example above, there are three blocks of parameters that can be configured in the plugin.

  • filePath: This parameter works in the same way as in the first option.
  • consumer, supplier and streamBridge: They are both configured in the same way and can receive the same parameters. These parameters are:
    • ids: With this parameter you can set the operationId that you want to be generated as subscriber or publisher. If this parameter is not defined for the consumer section, all the subscribe operations defined in the YML file, will be generated. If only one of supplier and streamBridge sections are defined, and this parameter is not defined inside it, all the publish operations defined in the YML file will be generated. If both supplier and streamBridge sections are defined, it`s needed to define which operations belongs to each category.
    • classNamePostfix: This parameter receives the name of the class that it's going to be generated containing the Beans. This parameter is optional, and by default the classes will be called Producer, StreamBridgeProducer and Subscriber.
    • modelNameSuffix: With this parameter you can set the suffix that is going to be used in the entities of the generated classes. For example if you set this to DTO, and there is a class named EntityClass, it will result as EntityClassDTO. This parameter is optional.
    • apiPackage: This parameter receive a package name, where the generated classes will be generated. This parameter is optional. Check how the apiPackage is setted for more information about how this parameter works, and the values it could have.
    • modelPackage: This parameter receive a package name, where the entities used for the generated classes are defined. As it's explained in the Mapper Section, those entities are usually auto-generated, so the plugin expects the modelPackage to be the package where them are included. Note that the plugin doesn't create the entities neither checks their existence, it takes their names from the YML file and assume that they are created by the user. As the previous parameter, this is also optional. Check how the modelPackage is setted for more information about how his parameter works, and the values it could have.

The configuration of consumer, supplier and streamBridge are independent. If only one of them is configured in the pom file, only that one will be generated.

Generated Sources Folder

There is also an independent parameter that affects to all the fileSpecs generated, which is called generatedSourcesFolder. This parameter expects to receive a string, that could include letters, numbers and -, with the name of the folder where generated sources by the plugin will be located.

By default, it's values is generated-sources, so the files will be in .../target/generated-sources/apigenerator/.... If you set another value in the pom.xml file, as in the example above, files will remain in .../target/sources-generated/apigenerator/....

How apiPackage is setted?

The api package could be set in three different ways.

  • User definition: The user provides a package name using the parameter in the pom.xml file.
  • GroupID from YML: If the user doesn't provide a package name, the plugin will try to use the groupId attribute from the YML file that is in use.
  • Default package name: If neither of the previous options were given, the plugin will use a default package name, that is stablished as net.coru.apigenerator.asyncapi.

How modelPackage is setted?

The model package could be set in four different ways.

  • User definition: The user provides a package name using the parameter in the pom.xml file.
  • Namespace from YML: If the user doesn't provide a package name, the plugin will check if the entity name definition in the YML file, includes a complete package name.
order/createCommand:
    subscribe:
      operationId: "subscribeOperation"
      message:
        $ref: '#/components/messages/net.coru.apigenerator.asyncapi.model.CreateOrder'
  • Namespace from Avro: If the user doesn't provide a package name, and the entity is defined by an Avro Schema, the plugin will check for a namespace attribute defined in the Avro file, and if there is, it will use it. The plugin expects to receive a relative path from the yml file folder.
order/created:
    publish:
      operationId: "publishOperation"
      message:
        $ref: 'path_to_Avro_file'
  • Default package name: If neither of the previous options were given, the plugin will use a default package name, that is stablished as net.coru.apigenerator.asyncapi.model.

Class Generation

Consumer and Supplier classes

Those are a pair of classes, separated by the directionality of the messages. They came from the plugin fully implemented by making reference to the interfaces of the next section. Their names could be modified using the classNamePostfix parameter specified on the Usage section, being by default Producer and Subscriber.

@Configuration
public class StreamTopicListenerConsumer {

    private final ISubscribeOperation subscribeOperation;

    protected StreamTopicListenerConsumer(final ISubscribeOperation subscribeOperation){
      this.subscribeOperation = subscribeOperation;
    }

    @Bean
    public Consumer<CreateOrder> consumerSubscribeOperation(){ 
      return value -> subscribeOperation.subscribeOperation(value); }
}

This sample class, is related to the previosly used YML file, and in it you could see that it came fully implemented, based on the related Interface that lets the personalitation and implementation to the user. Also, in this example is possible to see how the YML attribute 'operationId' is used to name the methods as Consumer'OperationId' or Publisher'OperationId'.

Method interfaces

Those are a group of interfaces that are related to the previous seen classes. There are as many as operations are defined in the YML file, and in the previous classes, so there is only one operation defined in each interface.

This layer is the only one that needs work by the end user, so it needs to implement these interfaces.

This interfaces are named following the "IOperationId" pattern, where 'OperationId' comes from the YML file definition of the channels section. Also the method is named as 'OperationId' as well as on the classes in the above section.

public interface ISubscribeOperation {

  void subscribeOperation(CreateOrder value);
}
Mapper

The entities used for the definitions both on the previous seen classes and this interfaces, are auto-generated entities, based on the same YML file. Because of that, they need to be mapped to a user defined entity using a mapper utility class.

This mapper must be defined by the user on it's own way to improve the personalitation capabilities of the plugin.

Down here you have an example of the mapper utility class as well as an simple class implementing the interface defined above.

@Mapper
public interface Mapper {
  Order map(net.coru.apigenerator.asyncapi.business_model.model.event.Order value);
}
Implementation
@Component
public class subscribeOperation implements ISubscribeOperation {
  private final Mapper mapper;

  public subscribeOperation(final Mapper mapper) {this.mapper = mapper;}

  @Override
  public void subscribeOperation(final Order value) {
    net.coru.apigenerator.asyncapi.business_model.model.Order orderMapped = mapper.map(value);
    //TODO: implement the functionality
  }
}

Stream Bridge class

In this case, there is only one class where all the selected operations will be included. It's name could be modified using the classNamePostfix parameter specified on the Usage section, being by default StreamBridgeProducer.

@Configuration
public class StreamBridgeProducer {

    private StreamBridge streamBridge;

    public void streamBridgeOperation(CreateOrder createOrder){
        streamBridge.send("publishOperation", createOrder);
    }
}

This sample class, is related to the previosly used YML file, and in it you could see that it came fully implemented.

Also, it's important to note that using Stream Bridge, the binding where the messages are going to be sent is included in the auto generated class. This is defined by the application properties using function, binders and bindings, as in the next example:

spring:
  kafka:
    bootstrap-servers: localhost:xxxx
    producer:
      client-id: peter
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
  cloud:
    function:
      definition: publishOperation
    stream:
      defaultBinder: kafka
      bindings:
        publishOperation:
          destination: orderCreated
      binders:
        kafka:
          defaultCandidate: true
          type: kafka
          producer-properties:
            key.serializer: org.apache.kafka.common.serialization.StringSerializer
            value.serializer: org.springframework.kafka.support.serializer.JsonSerializer

Because the plugin cannot access the application properties, the name of the corresponding binding must be used as the channel identifier in the YML file that's setted on the plugin configuration, as you can see on the next extract:

channels:
  publishOperation:
    subscribe:
      operationId: "streamBridgeOperation"
      message:
        $ref: '#/components/messages/CreateOrder'

Due to the limitations on topics naming, the identifier of the channels that are going to be used as Stream Bridge publishers, only could include - or . as separators, slash / is not allowed.

OpenApi Generator

Getting Started

In order to get this plugin working, you need the following things installed in your computer:

  • Java 11 Version
  • Maven

Depending on the approach with which you are going to use the plugin, other dependencies will be necessary, for example:

  • spring-boot-starter-webflux, in case you want to implement an API with responses in Mono/Flux Reactor types or use them for external calls through Spring WebClient.

After you have these installed, you need to add this plugin in your pom.xml file. Here is an example of a basic configuration:

<plugin>
  <groupId>net.coru</groupId>
  <artifactId>scs-multiapi-maven-plugin</artifactId>
  <version>3.0.2</version>
  <executions>
    <execution>
        <goals>
            <goal>openapi-generation</goal>
        </goals>
        <configuration>
            <fileSpecs>
                <fileSpec>
                    <filePath>${project.basedir}/src/main/resources/api/api.yml</filePath>
                    <apiPackage>net.coru.apigenerator.openapi.api</apiPackage>
                    <modelPackage>net.coru.apigenerator.openapi.api.model</modelPackage>
                    <modelNameSuffix>DTO</modelNameSuffix>
                </fileSpec>
            </fileSpecs>
        </configuration>
    </execution>
  </executions>
</plugin>

Initial Considerations

Before using this plugin we have to warn that not all the complexity and support offered by the use of swagger.io yml files is supported.

Since 1.1.0 version, we support the definition of parameters in both Path and Operation object, but you can only define it in one of them. If you specify them in both objects it will trigger an Exception.

We establish here some of these options that are not yet supported and that will be added to this plugin as time goes by and the existing need among users.

  • The use of parameters defined in the component element by reference.

  • The use of parameters with content tag.

  • Using Multiple Authentication Types within the security options both at an operational and general level.

  • The use of OAuth 2 and OpenID Connect Discovery Authentication Types.

Usage

This plugin allows us to create multiple apis with just one maven clean install execution, in this way the user can configure several fileSpecs tags with different uses, thus generating Apis in the two possible modes: send or receive calls, depending on the options of configuration selected in said fileSpecs.

<configuration>
    <fileSpecs>
        <fileSpec>
            <filePath>${project.basedir}/src/main/resources/api/api.yml</filePath>
            <apiPackage>net.coru.apigenerator.openapi.api</apiPackage>
            <modelPackage>net.coru.apigenerator.openapi.api.model</modelPackage>
            <modelNameSuffix>DTO</modelNameSuffix>
        </fileSpec>
    </fileSpecs>
</configuration>

To customize these fileSpecs tags we are going to specify them inside the configuration tag, we must declare the fileSpecs tag that contains all files that will be used. Each fileSpec has their own configuration:

Name Description Example
filePath Path where the yaml is located ${project.basedir}/src/main/resources/api/api.yml
apiPackage Path where the api interface will be located net.coru.apigenerator.openapi
modelPackage Path where the models will be located net.coru.apigenerator.openapi.model
modelNamePrefix Prefix that will be used ahead of every model´s name Api
modelNameSuffix Suffix that will be used after every model´s name DTO
callMode Boolean value to decide if you want to generate the api for external calls. Use RestClient by default. It´s initialized to false by default false
useTagsGroup Boolean value to decide if using tags instead of an URL for group the API. It´s initialized to false by default false
useLombokModelAnnotation Boolean value to decide if you want your models with Lombok or not It´s initialized to false by default false
isReactive Boolean value to decide if you want to generate the api with responses in Mono/Flux Reactor types. If callmode = true use WebClient instead of RestClient. It´s initialized to false by default false

As the configuration options already indicate, the data model will also be created within the specified path.This model will be created with the indicated prefixes and suffixes and the instances and imports will be made to that model within the corresponding Api.

There are two properties configured outside the fileSpecs, the path where the RestClient and the WebClient will be located, if this option is set in any of the fileSpecs, and the name of the folder where the generated sources will be saved in the api of the project.

Name Description Example
clientPackage Path where the RestClient and/or WebClient are located net.coru.apigenerator.openapi.client
generatedSourcesFolder Name of the folder, inside target, where the files will be located. By defaut it's generated-sources generated-sources
overwriteModel Boolean value to decide if you want your models to be overwritten if two or more models have the same name. True means that models will be overwritten and if false is set, it will throw an exception if two models share the same name. It is initialized to false by default false

We must clarify that the options to make calls are configured under the RestClient or WebClient specifications as indicated above in the configuration options. If several of the APIs to be generated are defined under the same call option, a single RestClient/Webclient will be generated for all of them, which is initialized with the specific options needed within the class that defines each API.

Usage considerations

This plugin has been implemented trying to behave like OpenApi Generator Tool but we decided to change the approach concerning the support of AllOfs, OneOfs and AnyOfs.

Every property that has been indicated in any of these types will be generated in the model entity.

The way the model will behave changes depending on whether it is an AllOf, or an AnyOf/OneOf:

If it is an AllOf, every property referenced will be treated as required regardless of which ones are defined in the "required" field of the allOf structure.

If it is an AnyOf or an OneOf, the plugin will only mark as required the properties that have been defined as such in the "required" field of these structures. After that, the constructor will check that at least one of the properties will have a value, nothing else, so it is up to the user to fulfill the restrictions he needs for the entity.

IMPORTANT NOTE: As previously stated, OneOf and AnyOf will behave the same, this means that OneOf will work the same way as an AnyOf.

You might also like...

A boilerplate project designed to work as a template for new microservices and help you get in touch with various useful concepts.

Microservice Reference Project This project is inspired by the idea to quickly create a production ready project with all the required infrastructure

Dec 17, 2022

A small companion library to Mixin, designed to help you write your Mixins in a more expressive and compatible way.

MixinExtras A small companion library to Mixin, designed to help you write your Mixins in a more expressive and compatible way. More information about

Jan 7, 2023

Team 5468's 2022 FRC robot code. This code is written in Java and is based off of WPILib's Java control system and utilizes a command based system

FRC 2022 Team 5468's 2022 FRC robot code. This code is written in Java and is based off of WPILib's Java control system and utilizes a command based s

Oct 4, 2022

Community extension to generate a Java client from the provided Camunda 7 OpenAPI descitpion and also warp it into Spring Boot

Camunda Engine OpenAPI REST Client Java and Spring Boot This community extension is a convenience wrapper around the generated Java client from the Ca

Dec 28, 2022

Z2Laser - a simple Java-based tool to convert Z movements in your G-Code files to Laser on or off commands

Z2Laser - a simple Java-based tool to convert Z movements in your G-Code files to Laser on or off commands

Feb 20, 2022

Community-Driven Game Server Development solution for Java Developers based on DEEPINTHINK MagOKO Project.

MagOKO Stack Community-Driven Game Server Development solution for Java Developers based on DEEPINTHINK MagOKO Project. License Copyright 2021-present

Jun 1, 2021

Java based open source static site/blog generator for developers & designers.

JBake JBake is a Java based open source static site/blog generator for developers. Documentation Full documentation is available on jbake.org. Contrib

Dec 30, 2022

tuya-spring-boot-starter helps you efficiently create cloud development projects regarding the OpenAPI or message subscription capabilities. You can put all the focus on business logic without taking care of server-side programming nor relational databases.

English | 中文版 tuya-spring-boot-starter helps you efficiently create cloud development projects regarding the OpenAPI or message subscription capabilit

Dec 26, 2022

OpenApi Generator - REST Client Generator

OpenApi Generator - REST Client Generator

Quarkus - Openapi Generator Welcome to Quarkiverse! Congratulations and thank you for creating a new Quarkus extension project in Quarkiverse! Feel fr

Jan 3, 2023
Releases('4.2.5')
Owner
Corunet
Corunet
IntelliJ plugin for continuous OpenAPI linting using the Spectral OpenAPI linter

Spectral IntelliJ Plugin This plugin is a wrapper for the tool Spectral, a linter for OpenApi schemas. It supports all Jetbrains IDEs starting at vers

Schwarz IT 19 Jun 6, 2022
OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)

OpenAPI Generator Master (5.4.x): 6.0.x (6.0.x): ⭐ ⭐ ⭐ If you would like to contribute, please refer to guidelines and a list of open tasks. ⭐ ⭐ ⭐ ‼️

OpenAPI Tools 14.8k Dec 30, 2022
OpenAPI JSON Schema Generator allows auto-generation of API client libraries with a focus on JSON schema given an OpenAPI Spec

OpenAPI JSON Schema Generator IMPORTANT: before the first release, one will need to build the project locally to use the enhancements, bug fixes in th

OpenAPI Tools 5 Dec 31, 2022
A library that automatically generates and manages configuration files based on classes.

sc-cfg SC-CFG is a simple, yet powerful library that automatically generate configuration files based on your classes. Compatible with Java 8+ and Kot

null 10 Nov 28, 2022
Maven plugin to check if dependencies in CycloneDX BOM files use only allowed licenses.

license-checker-cyclonedx-maven-plugin Maven plugin to check if dependencies in CycloneDX BOM files use only allowed licenses. Quick start guide 1. Ad

null 3 Sep 17, 2022
Automatic creation of simple CRUD API of Spring boot and JPA project.

fast-crud Automatic creation of simple CRUD API of Spring boot and JPA project.

JinHwanKim 18 Oct 23, 2022
Caches datapack- and server resources to make world creation faster.

antiresourcereload Caches datapack- and server resources to make world creation faster. Legalisation Status Legal (1.0.2) Description I found out abou

Wurgo 13 Nov 18, 2022
A networking library for LibGDX utilizing Netty allowing easy creation of multiplayer games.

Lunar What is Lunar? Lunar is a networking library for LibGDX. With lunar you can easily create multiplayer games quickly and efficiently. Lunar provi

Vrekt 28 Nov 25, 2022
Alibaba Cloud Dedicated KMS Transfer SDK for Java can help Java developers to migrate from the KMS keys to the Dedicated KMS keys.

Alibaba Cloud Dedicated KMS Transfer SDK for Java Alibaba Cloud Dedicated KMS Transfer SDK for Java can help Java developers to migrate from the KMS k

Alibaba Cloud 3 May 12, 2022
An API Library that provides the functionality to access, manage and store device topologies found in JSON files using Java and Maven Framework

Topology API ?? About An API library which provides the functionality to access, manage and store device topologies. ?? Description Read a topology fr

Abdelrahman Hamdy 2 Aug 4, 2022