OpenAPI generatorを試してみる

# OpenAPI

OpenAPI、正確にはOpenAPI SpecificationはREST APIの定義を記述するための規格です。
つまり、APIサーバがどのような挙動をするのかを記した設計書のようなものです。
2015年末まではSwagger Specificationと呼ばれていたので、こちらの方を知っている方もいるかもしれません。

さて、これがあると何が嬉しいかというといろいろ嬉しいことがあるのですが、主なメリットは次の3点です。

1. APIを決まったフォーマットで規定できる
2. そのままAPIドキュメントになる
3. API定義からサーバやクライアントのコード生成ができる

Swagger時代からSwagger Specificationに関連したツールが出回っており、ツールを利用することで手軽に上記のメリットを享受できるようになっています。

| メリット | 代表的なツール |
|---:|---:|
| APIを決まったフォーマットで規定できる | OpenAPI Specification <br> Swagger Specification | 
|  APIドキュメントになる | Swagger UI |
| コード生成ができる | OpenAPI generator <br> Swagger Codegen |

他にもOpenAPI Specificationを利用した様々なツールが作成されているので、気になる方は公式レポジトリのリストを覗いてみてください。
https://github.com/OAI/OpenAPI-Specification/blob/master/IMPLEMENTATIONS.md

本稿ではSwaggerとOpenAPIやバージョン2と3の話はOpenAPI generatorの話から脱線してしまうため触れないこととし、OpenAPI v3.0を対象とします。

## OpenAPI Specification

JSONまたはYAML形式でAPIのrequest endpointやparameter, responseを記述することができます。
例えば、次のようなYAMLファイルを記述することで、APIが `/pets` というend pointを持つことや、`200 OK`のレスポンスに`id`と`name`を持つリストがjson形式で返ってくることがわかります。

```yaml:simple-petstore
openapi: "3.0.0"
info:
  version: 1.0.0
  title: Swagger Petstore
  license:
    name: MIT
servers:
  - url: http://petstore.swagger.io/v1
paths:
  /pets:
    get:
      summary: List all pets
      operationId: listPets
      tags:
        - pets
      parameters:
        - name: limit
          in: query
          description: How many items to return at one time (max 100)
          required: false
          schema:
            type: integer
            format: int32
      responses:
        '200':
          description: A paged array of pets
          headers:
            x-next:
              description: A link to the next page of responses
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pets"
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
components:
  schemas:
    Pet:
      required:
        - id
        - name
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
        tag:
          type: string
    Pets:
      type: array
      items:
        $ref: "#/components/schemas/Pet"
    Error:
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string
```

このYAMLは [こちらのsample](https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml)を簡略化したもの。

# OpenAPI generator

OpenAPI generatorは先程述べたメリットの3番目、OpenAPI Specificationを元にクライアントやサーバのコードを生成するソフトウェアです。
もともとSwaggerの時代からSwagger CodeGenとしてコード生成のツールは提供されており今でも利用可能なのですが、どうも開発者同士で反りが合わなかったらしくコミュニティ主導のツールとしてOpenAPI Specificationは誕生しました。
OpenAPI generatorの開発者がそのあたりの経緯を書いています。(日本語)
https://ackintosh.github.io/blog/2018/05/12/openapi-generator/

私が2018年9月頃にSwagger CodeGenを触った感触でやはりSwagger CodeGenはOpenAPI Specification v3.0への対応が遅れていると感じたため、2019年3月現時点でもOASv3.0を利用する場合は様々な言語に対応しているOpenAPI generatorを利用するのが良さそうです。

https://github.com/OpenAPITools/openapi-generator

## 環境

利用方法はいくつかあるのですが、今回はdocker imageを利用します。
macでDocker Desktop for Macを利用しています。

```bash

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.3
BuildVersion:	18D109
$ docker --version
Docker version 18.09.1, build 4c52b90
```

生成したコードを出力する場所を作っておきます。
また、先程の簡略化petstore.yamlをカレントディレクトリにおいておきます。

```
$ mkdir openapi_generator_sample
$ cd openapi_generator_sample/
$ cp /somewhere/petstore.yaml ./
```

## コード生成

[こちら](https://github.com/OpenAPITools/openapi-generator#16---docker)にかかれている通り、次のコマンドで Flask/Python のサーバサイドコードが生成されます。

```bash:Flaskコード生成
$ GENERATOR=python-flask
$ docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/petstore.yaml -g ${GENERATOR} -o /local/out/${GENERATOR}
```

長いですが、dockerで`openapitools/openapi-generator-cli`イメージのgenerateコマンドを呼び出して、次のオプションを渡しているだけです。

| オプション | 意味 |
|---:|---:|
|`-g`| 生成するコードの種類を指定|
|`-i`| OpenAPI Specificationのyamlファイルを指定 |
|`-o`| 出力する場所を指定|

上記コードを実行すると `out/python-flask` フォルダの中にFlaskのコード(ボイラープレート/scaffold)が生成されます。

```bash:Flaskアプリ
$ tree out/python-flask/
out/python-flask/
├── Dockerfile
├── README.md
├── git_push.sh
├── openapi_server
│   ├── __init__.py
│   ├── __main__.py
│   ├── controllers
│   │   ├── __init__.py
│   │   ├── pets_controller.py
│   │   └── security_controller_.py
│   ├── encoder.py
│   ├── models
│   │   ├── __init__.py
│   │   ├── base_model_.py
│   │   ├── error.py
│   │   └── pet.py
│   ├── openapi
│   │   └── openapi.yaml
│   ├── test
│   │   ├── __init__.py
│   │   └── test_pets_controller.py
│   └── util.py
├── requirements.txt
├── setup.py
├── test-requirements.txt
└── tox.ini

5 directories, 21 files
```

## 対応言語

別の言語でもGENERATOR部分を変えることによって簡単に同等のコードを生成できます。例えばRuby on Railsの場合は次のようにします。

```bash:RoRコード生成
$ GENERATOR=ruby-on-rails
$ docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/petstore.yaml -g ${GENERATOR} -o /local/out/${GENERATOR}
```

```bash:RoRアプリ

$ tree out/ruby-on-rails/
out/ruby-on-rails/
├── Dockerfile
├── Gemfile
├── README.md
├── Rakefile
├── app
│   ├── channels
│   │   └── application_cable
│   │       ├── channel.rb
│   │       └── connection.rb
│   ├── controllers
│   │   ├── application_controller.rb
│   │   └── pets_controller.rb
│   ├── jobs
│   │   └── application_job.rb
│   ├── mailers
│   │   └── application_mailer.rb
│   ├── models
│   │   ├── application_record.rb
│   │   ├── error.rb
│   │   └── pet.rb
│   └── views
│       └── layouts
│           ├── mailer.html.erb
│           └── mailer.text.erb
├── bin
│   ├── bundle
│   ├── rails
│   ├── rake
│   ├── setup
│   └── update
├── config
│   ├── application.rb
│   ├── boot.rb
│   ├── cable.yml
│   ├── database.yml
│   ├── environment.rb
│   ├── environments
│   │   ├── development.rb
│   │   └── production.rb
│   ├── initializers
│   │   ├── active_record_belongs_to_required_by_default.rb
│   │   ├── application_controller_renderer.rb
│   │   ├── backtrace_silencers.rb
│   │   ├── callback_terminator.rb
│   │   ├── cors.rb
│   │   ├── filter_parameter_logging.rb
│   │   ├── inflections.rb
│   │   ├── mime_types.rb
│   │   ├── ssl_options.rb
│   │   └── to_time_preserves_timezone.rb
│   ├── locales
│   │   └── en.yml
│   ├── puma.rb
│   ├── routes.rb
│   ├── secrets.yml
│   └── spring.rb
├── config.ru
├── db
│   ├── migrate
│   │   └── 0_init_tables.rb
│   ├── schema.rb
│   └── seeds.rb
├── docker-entrypoint.sh
├── lib
│   └── tasks
├── log
├── public
│   ├── 404.html
│   ├── 422.html
│   ├── 500.html
│   ├── apple-touch-icon-precomposed.png
│   ├── apple-touch-icon.png
│   ├── favicon.ico
│   └── robots.txt
├── test
│   └── test_helper.rb
├── tmp
│   ├── cache
│   ├── pids
│   ├── restart.txt
│   └── sockets
└── vendor

26 directories, 56 files
```

GENERATORに入れる値は次のコマンドで調べることができます。
Django等対応していないフレームワークもありますが、結構充実しています。

```bash:listコマンド
$ docker run --rm openapitools/openapi-generator-cli list
api-generator-cli list
The following generators are available:

CLIENT generators:
    - ada
    - android
    - apex
    - bash
    - c
    - clojure
    - cpp-qt5-client
    - cpp-restsdk
    - cpp-tizen
    - csharp
    - csharp-dotnet2
    - csharp-netcore
    - dart
    - dart-jaguar
    - eiffel
    - elixir
    - elm
    - erlang-client
    - erlang-proper
    - flash
    - go
    - groovy
    - haskell-http-client
    - java
    - javascript
    - javascript-closure-angular
    - javascript-flowtyped
    - jaxrs-cxf-client
    - jmeter
    - kotlin
    - lua
    - objc
    - perl
    - php
    - powershell
    - python
    - r
    - ruby
    - rust
    - scala-akka
    - scala-gatling
    - scala-httpclient-deprecated
    - scalaz
    - swift2-deprecated
    - swift3-deprecated
    - swift4
    - typescript-angular
    - typescript-angularjs
    - typescript-aurelia
    - typescript-axios
    - typescript-fetch
    - typescript-inversify
    - typescript-jquery
    - typescript-node
    - typescript-rxjs


SERVER generators:
    - ada-server
    - aspnetcore
    - cpp-pistache-server
    - cpp-qt5-qhttpengine-server
    - cpp-restbed-server
    - csharp-nancyfx
    - erlang-server
    - go-gin-server
    - go-server
    - graphql-nodejs-express-server
    - haskell
    - java-inflector
    - java-msf4j
    - java-pkmst
    - java-play-framework
    - java-undertow-server
    - java-vertx
    - jaxrs-cxf
    - jaxrs-cxf-cdi
    - jaxrs-cxf-extended
    - jaxrs-jersey
    - jaxrs-resteasy
    - jaxrs-resteasy-eap
    - jaxrs-spec
    - kotlin-server
    - kotlin-spring
    - nodejs-server
    - php-laravel
    - php-lumen
    - php-silex
    - php-slim
    - php-symfony
    - php-ze-ph
    - python-aiohttp
    - python-blueplanet
    - python-flask
    - ruby-on-rails
    - ruby-sinatra
    - rust-server
    - scala-finch
    - scala-lagom-server
    - scalatra
    - spring


DOCUMENTATION generators:
    - cwiki
    - dynamic-html
    - html
    - html2
    - openapi
    - openapi-yaml


SCHEMA generators:
    - mysql-schema


CONFIG generators:
    - apache2
    - graphql-schema
```

## サーバを動かしてみる

生成したコードはすぐに起動できるようになっています。
試しに生成したばかりのFlaskを起動してみます。

```bash

$ cd out/python-flask/
$ docker build -t openapi_server .
$ docker run -p 8080:8080 openapi_server
```

これで http://localhost:8080/v1/pets にアクセスして次のようなレスポンスが得られたら成功です。
<img width="564" alt="スクリーンショット 2019-03-25 23.56.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/266939/e046dc7c-5647-5cae-8e4b-6c2781ea2414.png">

## その先

OpenAPI generatorで生成できるサーバサイドのコードはロジックが記述されていません。API定義から生成したので当たり前ですね。そのため、実際にAPIサーバとして利用するためにはこのあとにロジックを書く必要があります。
しかし自動生成したコードにロジックを書くと、API定義に変更があったときにコード側が自動生成で追随できなくなってしまいます。この問題に関する話題はまた別の記事で書きます。

# まとめ

今回はOpenAPIの周囲のツールのうち、OpenAPI generatorを触ってみました。
OpenAPI generatorではAPI定義から様々なAPIサーバの雛形を簡単に生成することができました。
API Specificationを先に書くことによって、フロントエンドとサーバサイドの同時開発ができる、コードの雛形の生成ができる、APIドキュメントを別途用意・メンテする必要がなくなるというメリットが享受できます。

個人的には`petstore.yaml`から雛形だけ作成して色々なWebフレームワークの実装の練習に利用しようかなと考えています。

# 参考

開発者の方の解説。すごくわかりやすかったです。
https://speakerdeck.com/akihito_nakano/gunmaweb34

より詳しく知りたい方には開発者によるeBookが出ていますので、こちらも参考にどうぞ。
[![REST APIのためのコード生成入門](https://static-2.gumroad.com/res/gumroad/3680469107511/asset_previews/f359f63a4feaf3abf15e1046515adf79/retina/ebook_20cover_20japanese-page-001.jpg)](https://gumroad.com/l/openapi_generator_ebook_jp)