OpenAPI Generator を使って Retorift x Coroutine x kotlinxSerialization のコードを自動生成してみた

## はじめに

OpenAPI Generator に以下のような [プルリク](https://github.com/OpenAPITools/openapi-generator/pull/6916) があげられていて
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165251/1ab513ec-5e6d-3c52-2b95-b0d765e466c2.png)
早速使ってみるしかない！
と思い、ローカルでビルドして実際に使ってみました :sunglasses: 

### 環境

>
- macOS Catalina バージョン 10.15.7
- git version 2.24.3 (Apple Git-128)
- Homebrew 2.5.10
  - Homebrew/homebrew-core (git revision 73b41; last commit 2020-11-15)
  - Homebrew/homebrew-cask (git revision 2f686; last commit 2020-11-15)
- Android Studio 4.1.1
  - Build #AI-201.8743.12.41.6953283, built on November 5, 2020
  - Runtime version: 1.8.0_242-release-1644-b3-6915495 x86_64
- IntelliJ IDEA 2020.2.3 (Ultimate Edition)
  - Build #IU-202.7660.26, built on October 6, 2020
  - Runtime version: 11.0.8+10-b944.34 x86_64

### OpenJDK 8 を使えるようにしておく

```bash
brew tap AdoptOpenJDK/openjdk
brew install adoptopenjdk-openjdk8

java -version
# openjdk version "1.8.0_275"
# OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_275-b01)
# OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.275-b01, mixed mode)
```

### 関連リポジトリ

ご参考までに〜
※ このリポジトリでは yaml の更新をすると自動でプルリクが投げられるように設定してます :shipit: 

- https://github.com/blendthink/linkmark-android
- https://github.com/blendthink/linkmark-api

## OpenAPI Generator のリポジトリをクローン

まず、リポジトリをクローンしてきて、プルリクのブランチにチェックアウトします

するとこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165251/085d09dd-4eb0-51cf-0b31-9e6529a2cf05.png)
ここで、Terminal を開き、以下のコマンドでプロジェクトをビルドします

```bash
./mvnw clean install
```

すると以下のように `modules/openapi-generator-cli/target` の直下に `openapi-generator-cli.jar` というファイルが作成されるので、これを API Client コードを自動したいプロジェクトのルートの直下に配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165251/34243c3b-ce28-5a86-df5d-702cdd426784.png)

## API 仕様書（ yaml ファイル） と API Client コードの自動生成用スクリプトを作成

次に、API Client コードのプロジェクトの直下に API の仕様を定めた yaml ファイルと自動生成用のスクリプトファイルを配置します。

自分は以下のように書きました。（各々で書く必要あり）

<details><summary>linkmark.yaml</summary><div>

```yaml
openapi: 3.0.0
info:
  title: Linkmark
  description: Linkmark
  version: 1.0.0
  license:
    name: Apache 2.0
    url: 'https://www.apache.org/licenses/LICENSE-2.0.txt'
  contact:
    name: blendthink
servers:
  - url: 'https://linkmark.com'
tags:
  - name: link
  - name: tag
  - name: token
paths:
  /tags:
    get:
      summary: '全てのタグを取得する'
      tags:
        - tag
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Tags'
      operationId: get-tags
      description: 'Key, Token の組み合わせで取得可能な全てのタグを取得する'
      parameters:
        - schema:
            type: string
            format: uuid
          in: query
          name: key
          required: true
        - schema:
            type: string
          in: query
          name: token
          required: true
    post:
      summary: 'tタグを作成する'
      tags:
        - tag
      operationId: post-tags
      responses:
        '200':
          description: OK
      parameters:
        - schema:
            type: string
            format: uuid
          in: query
          name: key
          required: true
        - schema:
            type: string
          in: query
          name: token
          required: true
      requestBody:
        content:
          text/plain:
            schema:
              $ref: '#/components/schemas/TagName'
      description: 'Key, Token の組み合わせで紐づけられたタグを作成する'
  /links:
    get:
      summary: '全てのリンクを取得する'
      tags:
        - link
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Links'
      operationId: get-links
      parameters:
        - schema:
            type: string
            format: uuid
          in: query
          name: key
          required: true
        - schema:
            type: string
          in: query
          name: token
          required: true
      description: 'Key, Token の組み合わせで取得可能な全てのリンクを取得する'
    post:
      summary: 'リンクを作成する'
      tags:
        - link
      operationId: post-links
      responses:
        '200':
          description: OK
      parameters:
        - schema:
            type: string
            format: uuid
          in: query
          name: key
          required: true
        - schema:
            type: string
          in: query
          name: token
          required: true
      description: 'Key, Token の組み合わせで紐づけられたリンクを作成する'
      requestBody:
        content:
          text/plain:
            schema:
              $ref: '#/components/schemas/Url'
  /tokens:
    post:
      summary: 'トークンを作成する'
      tags:
        - token
      operationId: post-tokens
      responses:
        '200':
          description: OK
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/Token'
              examples:
                token:
                  value: Atc|MQEWYJxEnP3I1ND03ZzbY_NxQkA7Kn7Aioev_OfMRcyVQ4NxGzJMEaKJ8f0lSOiV-yW270o6fnkI
      parameters: []
      description: 'トークンを作成する'
      requestBody:
        content:
          text/plain:
            schema:
              $ref: '#/components/schemas/Key'
            examples:
              key:
                value: 93abd516-f6b1-4108-b7af-d416f4b59f5d
components:
  schemas:
    Key:
      type: string
      title: Key
      format: uuid
      example: 93abd516-f6b1-4108-b7af-d416f4b59f5d
    Token:
      type: string
      title: Token
      example: Atc|MQEWYJxEnP3I1ND03ZzbY_NxQkA7Kn7Aioev_OfMRcyVQ4NxGzJMEaKJ8f0lSOiV-yW270o6fnkI
    Url:
      type: string
      title: Url
      format: uri
      example: 'https://example.com'
    TagName:
      type: string
      title: TagName
      example: kotlin
    Tag:
      title: Tag
      type: object
      properties:
        id:
          type: integer
          format: int64
        name:
          $ref: '#/components/schemas/TagName'
      required:
        - id
        - name
    Tags:
      title: Tags
      type: array
      items:
        $ref: '#/components/schemas/Tag'
    Link:
      title: Link
      type: object
      properties:
        id:
          type: integer
          format: int64
        url:
          $ref: '#/components/schemas/Url'
        tags:
          $ref: '#/components/schemas/Tags'
      required:
        - id
        - url
        - tags
    Links:
      title: Links
      type: array
      items:
        $ref: '#/components/schemas/Link'
  securitySchemes: {}
```

</div></details>

<details><summary>update_api_client.sh</summary><div>

```bash
#!/bin/bash

SCRIPT_DIR=$(cd $(dirname $0); pwd)

# API Client のパッケージ
API_CLIENT_PACKAGE=dev.honwakalab.linkmark.apiclient

# linkmark の API Client のパッケージ
LINKMARK_API_CLIENT_PACKAGE=$API_CLIENT_PACKAGE.linkmark

# linkmark の API Client の api, invoker, model のパッケージ
LINKMARK_API_CLIENT_API_PACKAGE=$LINKMARK_API_CLIENT_PACKAGE.api
LINKMARK_API_CLIENT_INVOKER_PACKAGE=$LINKMARK_API_CLIENT_PACKAGE.invoker
LINKMARK_API_CLIENT_MODEL_PACKAGE=$LINKMARK_API_CLIENT_PACKAGE.model

# Open API Generator の出力先
OPEN_API_GENERATOR_OUTPUT_DIR=$SCRIPT_DIR/apiclient/build/openApiGenerator/linkmark

# API Client の出力先
API_CLIENT_OUTPUT_DIR=$OPEN_API_GENERATOR_OUTPUT_DIR/src/main/kotlin/dev/honwakalab/linkmark/apiclient

# API Client infrastructure の出力先
API_CLIENT_INFRASTRUCTURE_OUTPUT_DIR=$API_CLIENT_OUTPUT_DIR/infrastructure

# API Client linkmark の出力先
API_CLIENT_LINKMARK_OUTPUT_DIR=$API_CLIENT_OUTPUT_DIR/linkmark

# プロジェクト内の API Client のディレクトリ
API_CLIENT_DIR=$SCRIPT_DIR/apiclient/src/main/kotlin/dev/honwakalab/linkmark/apiclient

# API Client の出力先を削除しておく
rm -rf $API_CLIENT_OUTPUT_DIR

# API Client 生成
java -jar openapi-generator-cli.jar generate \
  --generator-name kotlin \
  --library jvm-retrofit2 \
  --output $OPEN_API_GENERATOR_OUTPUT_DIR \
  --input-spec $SCRIPT_DIR/linkmark.yaml \
  --package-name $API_CLIENT_PACKAGE \
  --api-package $LINKMARK_API_CLIENT_API_PACKAGE \
  --invoker-package $LINKMARK_API_CLIENT_INVOKER_PACKAGE \
  --model-package $LINKMARK_API_CLIENT_MODEL_PACKAGE \
  --additional-properties collectionType=list,dateLibrary=java8,enumPropertyNaming=UPPERCASE,serializationLibrary=kotlinx_serialization,useCoroutines=true

# API Client 生成に成功したら、プロジェクト内の API Client のディレクトリを空っぽにする
rm -rf $API_CLIENT_DIR
mkdir -p $API_CLIENT_DIR

# 必要なファイルをコピーする
cp -r $API_CLIENT_INFRASTRUCTURE_OUTPUT_DIR $API_CLIENT_DIR
cp -r $API_CLIENT_LINKMARK_OUTPUT_DIR $API_CLIENT_DIR

```

</div></details>


## スクリプトを実行して、API Client コードを自動生成！！

あとは、先ほど作成していたスクリプトを実行するだけです！ :sunglasses: 

```bash
bash ./update_api_client.sh
```

こんな感じになり成功です :sunny: 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165251/8daa7871-cfa3-b491-be5d-c01a15ae8296.png)

## おわりに

現時点（2020/11/26）ではまだ、マージ・リリースされてはいませんが、もう少しで 5.0.0 がリリースされそうなので、それに間に合えば、ガンガンお仕事でも使っていこうかなと思っています :sunglasses: 

皆さんもぜひ快適な自動化ライフを！！