SaFi Bank Space : Plugin: generated-client-publisher-plugin vs. generated-client-publisher-plugin-java

GitHub:

https://github.com/SafiBank/SaFiMono/tree/main/common/generated-client-publisher-plugin

https://github.com/SafiBank/SaFiMono/tree/main/common/generated-client-publisher-plugin-java

Overview

These two Gradle plugins are used for automating the generation of HTTP client APIs (for the services that expose such API). Being very similar, these plugins are documented together, to allow a compare-and-contrast approach.

Background

The generated-client-publisher-plugin was initially developed as part of the effort to standardize the REST API versioning approach (more info can be found here: REST API versioning and client lib generation). The main problem with this plugin is that the generated clients were dependent on micronaut, and tied to the specific micronaut version of the service, which means that both the server (on which the client was built) and the consumer must be on the same version of micronaut. By extrapolation, this means that all microservices of the bank had to be on the same micronaut version.

To remove this high coupling, a new plugin was created (generated-client-publisher-plugin-java), that would generate client APIs without depending on micronaut (exploration phase documented here: Using another generator for API clients), instead generating pure Java code. To make the transition smoother, these plugins are supposed to be used together, by each service. The result is that each service will be publishing two different client APIs, thus allowing their consumers to switch at their own pace. This PR will add the Java generator plugin to all services: https://github.com/SafiBank/SaFiMono/pull/7715

1. Micronaut-based generator

Gradle plugin that unifies and simplifies publishing of generated openapi clients to maven repository.

How it works

OpenapiClientPublisherPlugin performs following actions:

  • applies OpenAPI generator gradle plugin to generate code based on the OpenAPI yml file

  • registers a new task called openApiGenerateAndBuild to generate the client library jar file

  • applies MavenPublishPlugin (for publishing in Maven format to Maven repository) and ArtifactRegistryGradlePlugin (for authentication against GCP artifact registry)

  • creates MavenPublication (configuration of how Gradle should publish something in Maven format) that will be later configured by user of this plugin

Usage

On the client/user (microservice that wants to call rest api) side, following steps need to be done:

  • repositories for plugins need to be set in settings.gradle.kts - apart from implicit gradlePluginPortal, we need to add our maven repository by specifying this top level property:

pluginManagement {
    repositories {
        gradlePluginPortal()
        maven {
            url = uri("https://asia-southeast1-maven.pkg.dev/safi-repos/safi-maven")
        }
    }
}
  • in build.gradle.kts, add the following plugin:

id("ph.safibank.common.generated.client.publisher") version "1.+"

  • in settings.gradle.kts, rootProject.name must be properly set - it will be used in the artifactIdof the resulting artifact

  • in gradle.properties, add:

group=ph.safibank.<servicename>

  • in build.gradle.kts, reference that property as:

group = project.properties["group"]!!

Outcome

The outcome of running gradle openApiGenerateAndBuild will be the generated client artifact (with proper name/version) present in lib directory and ready to be published to maven repository:

  • maven repo is defined by OpenapiClientPublisherPlugin.mavenRepoUri ("https://asia-southeast1-maven.pkg.dev/safi-repos/safi-maven" at the time of writing)

  • groupId is defined by $group.client where $group is the project's group

  • artifactId in defined as $name-api-client where $name is the project's name

  • version is defined in openapi.properties with the micronaut.openapi.expand.api.version property

2. Java-based generator

Gradle plugin that unifies and simplifies publishing of generated openapi clients to maven repository. Unlike generated-client-publisher-plugin (based on the java-micronaut-client generator type), is generates pure Java code (uses java generator type) and does not depend on micronaut.

How it works

OpenapiClientPublisherPlugin performs following actions:

  • applies OpenApi generator gradle plugin to generate code based on the OpenApi yml file

  • registers a new task called 'openApiGenerateAndBuildJava' to generate the client library jar file

  • applies MavenPublishPlugin (for publishing in Maven format to Maven repository) and ArtifactRegistryGradlePlugin (for authentication against GCP artifact registry)

  • creates MavenPublication (configuration of how Gradle should publish something in Maven format) that will be later configured by user of this plugin

Usage of Plugin

  • add the following plugin in build.gradle.kts:

id("ph.safibank.common.generated.client.publisher.java") version "1.+"

  • for now, it is expected that this plugin will be used alongside the micronaut generator - keep this line in build.gradle.kts:

id("ph.safibank.common.generated.client.publisher") version "1.+"

  • this will ensure that both client APIs are generated (java-micronaut-client and pure java versions)

  • for the motivation on using these plugins in the first place - see README.md for generated-client-publisher-plugin

Outcome

The outcome of running gradle openApiGenerateAndBuildJava will be generated client artifact (with proper name/version) present in lib directory and ready to be published to maven repository:

  • maven repo is defined by OpenapiClientPublisherPlugin.mavenRepoUri ("https://asia-southeast1-maven.pkg.dev/safi-repos/safi-maven" at the time of writing)

  • groupId is defined by $group.client where $group is the project's group

  • artifactId in defined as $name-api-client-java where $name is the project's name

Usage of Client

  • this version of the generated client cannot access application.yml, so it reads url directly from environment. So everything that needs to be done is to have defined environment variable SAFI_<service_name>_URL.

  • a wrapper with the name <service_name>Wrapper is also generated, which initializes the client with the service URL

  • in the consuming service, the recommended way of usage is to create a client/Clients.kt file, containing subclasses of all the generated clients, each marked as @Singleton:

package ph.safibank.infobipemailgateway.client

import jakarta.inject.Singleton
import ph.safibank.accountmanager.java.client.AccountManagerWrapper

@Singleton class AccountManagerClient(): AccountManagerWrapper()
@Singleton class CustomerManagerClient(): CustomerManagerWrapper()
[...]
  • now, these clients are injectable throughout the owning service

  • these clients are not drop-in replacements of the java-micronaut-client ones - expect that minor changes are needed, due to different APIs of the generated classes

    • e.g.: java-micronaut-client generates constructors with parameters vs. java uses builders