🏝️

OpenAPI Generator Gradle PluginsをAndroidプロジェクトに導入する

2024/04/26に公開

先日、OpenAPI Generator Gradle Pluginsの導入を行いました。
コードの生成自体は非常に役に立ったのですが、詰まるところがあったため記事に起こします。

OpenAPI Generatorとは

OpenAPI Specificationに従って書かれたyamlから、通信部分のコードを生成してくれるプログラムです。本家のGithubから対応している言語など確認することができます。

依存関係を追加

OpenAPI Generator Gradle Pluginsはその名の通り、Gradleのプラグインとして用意されています。

build.gradle.ktsに以下の通りプラグインへの依存関係を記述します。

build.gradle.kts
plugins {
        // 投稿時の最新バージョン
    id("org.openapi.generator") version "7.5.0"
}

projectappのどちらでも動作しますが、複数モジュールで利用するときはバージョンが衝突しないように気をつける必要があります。

これでタスクが利用可能になりました。

$ ./gradlew task
OpenAPI Tools tasks
-------------------
openApiGenerate - Generate code via Open API Tools Generator for Open API 2.0 or 3.x specification documents.
openApiGenerators - Lists generators available via Open API Generators.
openApiMeta - Generates a new generator to be consumed via Open API Generator.
openApiValidate - Validates an Open API 2.0 or 3.x specification document.

build.gradle.ktsにタスクの設定

Gradleのタスクとしては定義されましたが、実際に使えるように設定をしてやる必要があります。

openApiGenerate

プラグインを導入することでopenApiGenerateという関数が使えるようになります。
この関数を使って設定を行っていきます。
以下に例を示します。

build.gradle.kts
openApiGenerate {
    # 生成したいプログラミング言語
    generatorName = "kotlin"

    # 生成されるコードのパッケージ構成
    packageName = "com.package"

    # 参照するyamlファイル
    inputSpec = "$rootDir/specs/api.yaml"

    # 生成されるコードの出力先
    outputDir = "$rootDir/data"

    # テンプレートファイルの格納先(後述)
    templateDir = "$rootDir/templates"

    # 通信に使うライブラリ
    library = "jvm-retrofit2"

    # Selializarに用いるライブラリなどの設定
    configOptions = mapOf("serializationLibrary" to "kotlinx_serialization")

    # suspend関数を有効化
    additionalProperties = mapOf("useCoroutines" to "true")
}

公式のREADME.mdにて詳しく記述されているので、こちらを参考にしつつプロジェクトに合わせて設定していきます。

以上の設定をしてやることでタスクとして使えるようになっているはずです。

./gradlew openApiGenerate
################################################################################
# Thanks for using OpenAPI Generator.                                          #
# Please consider donation to help us maintain this project 🙏                 #
# https://opencollective.com/openapi_generator/donate                          #
#                                                                              #
# This generator's contributed by Jim Schubert (https://github.com/jimschubert)#
# Please support his work directly via https://patreon.com/jimschubert 🙏      #
################################################################################
Successfully generated code to {path}

無事にタスクが終わるとoutputDirにて定義した場所にモジュールが追加されると思います。
あとはこのモジュールをsettings.gradle.ktsに追記することで利用可能です。

settings.gradle.kts
include(":feature:post")

これで完成としてもいいのですが、微妙に使いづらいところがちょこちょこあります。
そこで、私がプロジェクトに導入させる際に行なったことを書いていきます。

ビルド時に生成されるようにする

初期状態だと、毎回openApiGenerateタスクを実行する必要があります。
それでもいいのですが、せっかくなのでビルド毎に実行されるように定義します。
以下のコードをbuild.gradle.ktsに追記します。

build.gradle.kts
tasks.getByName("preBuild") {
    dependsOn("openApiGenerate")
}

このようにすることで、ビルド前に走るようになってくれます。

gradleファイルの管理

OpenAPI Generatorではコマンドを実行することでモジュールを生成してくれます。
それ自体はいいのですが、そこで生成されてくるbuild.gradleの内容が微妙に古かったりします。
以下に一部抜粋します。

build.gradle
group 'org.openapitools'
version '1.0.0'

wrapper {
    gradleVersion = '7.5'
    distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}

buildscript {
    ext.kotlin_version = '1.8.10'
    ext.retrofitVersion = '2.10.0'
    // 6.13.0 is the latest stable release that supports JDK8
    ext.spotless_version = "6.13.0"

    repositories {
        maven { url "https://repo1.maven.org/maven2" }
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
        classpath "com.diffplug.spotless:spotless-plugin-gradle:$spotless_version"
    }
}

執筆当時では、Gradleは8.7、Kotlinは1.9.23が登場しており、古いバージョンを使っていることがわかります。とはいえ、このコードも自動生成されるので書き直すわけにもいきません。

そこで私のプロジェクトでは、buildファイルに入れることでソースコードのみ利用する形をとることにしました。

まず、生成コードの配置場所をbuildファイル内に変更します。

build.gradle.kts
    # 生成されるコードの出力先
+    outputDir = "${layout.buildDirectory.get()}/generated/openapi"
-    outputDir = "$rootDir/data"

これでタスクを実行するとbuild/generated/openapiに生成されたコードが配置されるようになります。

ただ、これだけではコードから呼び出すことができないため以下のようなコードを追記します。

build.gradle.kts
kotlin.sourceSets.main {
    kotlin.srcDir("${layout.buildDirectory.get()}/generated/openapi/src/main")
}

上の設定をすることでmainから生成されたコードを呼び出すことができるようになります。

ここまでのステップを踏むことで、ソースコードのみを利用することが可能になりました。
使うライブラリはタスクを定義したbuild.gradle.ktsで定義してやる必要があります。

build.gradle.kts
dependencies {
    # 執筆当時の最新バージョン
    api("com.squareup.retrofit2:retrofit:2.11.0")
}

テンプレートを上書き

コードが自動生成されるのは嬉しいのですが、偶に意図していないコードが生成されることがあります。
そのときはテンプレートを上書きしてやることで修正する必要があります。

私の場合、以下のようなケースに衝突しました。

本ケースの場合、import文を書き換えてやる必要があります。

build.gradle.ktsにて記述した場所にテンプレートを格納するフォルダを作成します。

build.gradle.kts
    # テンプレートファイルの格納先(後述)
    templateDir = "$rootDir/templates"

次にテンプレートファイルをコピペします。
本家サイトにてテンプレートが公開されていますのでこちらから必要なテンプレートだけコピーして貼り付けていきます。
今回のケースだとlibraries/jvm-retrofit2/infrastructure/ApiClient.mustacheです。

注意事項として、ファイルだけではなくディレクトリ構造もコピーする必要があります。今回の場合、templates/libraries/jvm-retrofit2/infrastructure/ApiClient.kt.mustacheにコードを格納する感じです。

あとはこのコードを書き換えてやれば完成です。58行目に該当コードがあるので書き換えます。

ApiClient.kt.mustache
+  import retrofit2.converter.kotlinx.serialization.asConverterFactory
-  import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory

これでタスクを実行すると書き換わっているはずです。

Discussion