API開発を助けたい OpenAPI Generator編

# はじめに

こんにちは。技術を学ぶことが好きな T です。

現場に初めて参画される方や API 開発をこれから行っていく方を助けたいという思いから、記事を作成させていただきました。
タイトルにあるように【OpenAPI Generator】というライブラリの設定に関して記載させていただきます。
微力でも皆様のお役に立てれば幸いです。
※ 最適解というわけでなく、一実装方法として確認いただけると幸いです。

### 対象者

- 現場に初めて参画される方
- 駆け出しエンジニア(WEBアプリの基礎がある程度わかっている)
- API 開発を実施する方
- schema 駆動開発を採用するプロジェクト

# OpenAPI Generator 概要

- OpenAPI
  - YAML また JSON で REST API の記述形式(フォーマット)です。
  - ある情報(Request)を送ることで、ある情報(Response)を返してくれる等を纏めたもの
  - Swagger というツール郡を活用すること、記述が楽になります。
  - OpenAPI and Swagger に関するサイト: [swagger.io](https://swagger.io/specification/)
  ```yaml
  openapi: 3.0.0
      info:
      title: member API
      description: Member API
      version: 1.0.0
      paths:
       /member:
          get:
          parameters:
              - in: path
              name: memberId
              description: Member ID
              example: Account-A
              required: true
              schema:
                  type: integer
                  format: int64
          summary: Fetch Member information
          operationId: getMemberInformation
          responses:
              200:
              description: Fetch member information
              content:
                  application/json:
                  schema:
                      $ref: '#/components/schemas/MemberResponse'
  ~~~
  ```
- OpenApiGenerator
  - OpenAPI をもとにスキーマ(Request, Response など)や Interface(Controller など)、Document 等を自動生成してくれます。
  - 対応言語も多く、typescript, python(fastApi) などでもお世話になってます。
  - コードの自動生成を行ってくれます。
  - 公式サイト: [openapi-generator](https://openapi-generator.tech/)
     
# OpenAPI Generator 設定方法

設定方法に関して、個人開発を行っているときに設定したものとなっています。    
よりよい設定はあると考えていますので、参考程度に確認してみてください。


- 想定される環境(当時確認した環境)
    - Java (version 17)
    - Gradle (version 7.5)
    - Spring Boot (version 2.7.3)
    - Editor: IntelliJ

- Gradle設定
    - ①等のコメントのある下部に記載されている行が必要です。
    - あくまで個人開発レベルでの設定です。最適解はプロジェクトごとにあると考えてください。
```gradle
plugins {
    id 'org.springframework.boot' version '2.7.3'
    id 'io.spring.dependency-management' version '1.0.13.RELEASE'
    id 'java'
    // ① openapi-generatorのPlugin宣言
    id "org.openapi.generator" version "6.2.0" 
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok:1.18.24'
    annotationProcessor 'org.projectlombok:lombok:1.18.24'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    // ②OpenAPIの依存性を記載します(下記2行)。Gradle install を行うことでライブラリをプロジェクトに紐づけましょう。
    implementation group: 'org.openapitools', name: 'openapi-generator-gradle-plugin', version: '6.2.0'
    implementation group: 'org.openapitools', name: 'jackson-databind-nullable', version: '0.2.3'
~~~~
}
~~~~
// ③Openapi Generatorの設定方法を記載する。 
openApiGenerate {
    generatorName = "spring"
    // 自動生成元となるopenapiの場所を指定しましょう。
    inputSpec = "$rootDir/specs/paths/openapi.yml".toString()
    // 自動生成したソースの置き場を指定しましょう。
    outputDir = "$buildDir/generated".toString()
    // 自動生成したControllerの配置位置を指定しましょう。
    apiPackage = "org.openapi.example.api"
    // 利用していないですが、公式サイトに則り記載しています。
    invokerPackage = "org.openapi.example.invoker"
    // 自動生成したModel(Request, Response等)の配置位置を指定しましょう。
    modelPackage = "org.openapi.example.model"
    // テストファイルの自動生成に関する設定。
    generateModelTests = false
    generateApiTests = false
    // ドキュメントの生成に関するオプション
    generateModelDocumentation = false
    generateApiDocumentation = false
    // その他オプション設定 [参照](https://openapi-generator.tech/docs/generators/java)
    configOptions = [
            // 自動生成時のJava Library
            dataLibrary          : "java8",
            // 自動生成時に作成されるドキュメントのプロパイダー
            documentationProvider: "springFox",
            // 自動生成時、Controllerはインターフェースのみ作成することを指示
            interfaceOnly        : "true"
    ]
}
// ④自動生成されたファイルを参照するために必要な記述です。
sourceSets {
    main {
        java {
            srcDir "${openApiGenerate.outputDir.get()}/src/main/java"
        }
        // SpringFoxの参照をしたいがために設定しています。
        resources {
            srcDir "${openApiGenerate.outputDir.get()}/src/main/resources"
        }
    }
}
```

# OpenAPI　Generator 実行

実行後、build/generated配下に自動生成されたソースを確認することができるかと思います。
Controller作成時に自動生成されたControllerのインターフェースを実装する形で開発を勧めていただけると幸いです。

- IntelliJ・Eclipseを利用
    - gradleタブ -> Tasks/openapi tools/openApiGenerateをクリック 

# OpenAPI Generator 私的おすすめ設定

Schema駆動開発で、絶大な効果を発揮するものと私は考えているため、自動生成されるAPIは手を加えないほうがいいです。       
手動でControllerのInterfaceやModelを修正してしまうと、Frontendの開発に影響が出てきます。     
誤って変更を手動で加えてしまうことを考慮して、プロジェクト時に再自動生成するのが適切かと思います。      
※ Interfaceを変更するのであれば、該当OpenAPIを修正後、再度自動生成するようにしましょう。
``` gradle
compileJava {
    dependsOn tasks.openApiGenerators
    source "${buildDir}/generated/openapi/src/main/java"
}
```
# OpenAPI Generator 発展編(複数OpenAPIからソース自動生成)

複数のOpenAPIから生成する方法に関して、見つけたやり方を紹介します。  
OpenAPI Generator Mergerを利用して複数APIを1つにまとめる手法です。  
※ 各サイトやサンプルサイトでは、1つのOpenAPIからの自動生成に関する記事しか見当たらなったので備忘録的な   
※ Gradleタスクをしっかり構築すればよりきれいな書き方ができるかと思いますが、気力負けしました。。。。。(泣

## OpenAPI Generator Merger 導入

- Gradle設定
``` gradle
plugins {
    id 'org.springframework.boot' version '2.7.3'
    id 'io.spring.dependency-management' version '1.0.13.RELEASE'
    id 'java'
    id "com.thinkimi.gradle.MybatisGenerator" version "2.4"
    id "org.openapi.generator" version "6.2.0"
    // ① OpenAPI Generator MergerをPluginsに設定する。
    id "com.rameshkp.openapi-merger-gradle-plugin" version "1.0.4"
}
~~~~
dependencies {
    ~~~~
    // ②OpenAPI Generator Mergerをプロジェクトに同期させます。
    openapi-merger-gradle-plugin.gradle.plugin
        implementation group: 'com.rameshkp.openapi-merger-gradle-plugin', name: 'com.rameshkp.openapi-merger-gradle-plugin.gradle.plugin', version: '1.0.4', ext: 'pom'
    ~~~~
}
~~~
// ③OpenAPI Mergeの設定
openApiMerger {
    // 統合したいOpenAPIの格納先を指定します。
    inputDirectory.set(file("$rootDir/specs/paths/".toString()))
    // 統合後のファイルの配置場所、名前、拡張子を設定しましょう。
    output {
        directory.set(file("$rootDir/specs/paths".toString()))
        fileName.set("openapi")
        fileExtension.set("yml")
    }
    // 統合後のファイルにて共通するパラメータを設定しています。
    openApi {
        openApiVersion.set("3.0.1")
        info {
            title.set("Open API MERGER")
            description.set("All files merged by open api merger")
            version.set("1.0.0")
        }
    }
}
~~~

// ④統合したOpenAPIの削除を行う処理
// 統合後のファイルが存在しているとMergeを行いために設定しています。
task deleteOpenApi(type: Delete) {
    delete files("$rootDir/specs/paths/openapi.yml")
}

// ⑤OpenAPIを再統合し、Generatorを実行します。
tasks.openApiGenerate {
    // 統合OpenAPIを削除します。
    dependsOn tasks.deleteOpenApi
    // OpenAPIを統合します。
    dependsOn tasks.mergeOpenApiFiles
    tasks.mergeOpenApiFiles.mustRunAfter tasks.deleteOpenApi
}
// ⑥Compile時に⑤が実行されるように変更しました。
compileJava {
    dependsOn tasks.openApiGenerate
    source "${buildDir}/generated/openapi/src/main/java"
}
```

#　終わりに

OpenAPI Generatorに関して紹介させていただきました。   
主には、Gradleの設定だったかと思いますが、参考になれば幸いです。
まだまだ未熟ゆえ誤り等、ございましたらご指摘いただけると幸いです。

OpenAPI Generatorに関しては、開発中ということもあり、まだまだかゆいところに手が届いていない用に感じます。
例えばAuthorizationなどのセキュリティに関する記述をした場合の自動生成がうまく行かなったり、自動生成時のConfigOptionとしてLombok追加し、自動生成するも、開発でうまく使うことができなかったりとします。
ただし、開発速度を上げてくれることは間違えありませんので、「こんなのあるんだ！」ぐらいで見ていただけるといいかなと思います。

※ ソースすべてや用語一つ一つを纏めることができず、申し訳ございません。
実装プロジェクトも用意しています(開発途中)ので、より詳しく実装を見たい方はどうぞ。[プロジェクト](https://github.com/konkon-T/experimentally-java)
### 参考サイト
 - [OpenAPI-Generator-Gradle-Plugin](https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-gradle-plugin)
 - [openapi-merger-plugin](https://github.com/kpramesh2212/openapi-merger-plugin)
 - [openapi-generator](https://openapi-generator.tech/)
 - [swagger.io](https://swagger.io/specification/)
 - [StackOverflow](https://stackoverflow.com/questions/58118614/how-to-generate-openapi-code-using-multiple-yaml-file)