Docker+Rails6+apipieを使ってAPI定義をSwagger UIに表示

## ゴール
1. API定義が書かれたファイルを自動生成する
1. docker-composeでSwagger UIのコンテナを管理する
1. ローカル環境のSwagger UIで、API定義の閲覧＆リクエスト/レスポンスを行う

## 環境
```
Ruby: 2.7.1
Rails: 6.0.3
```
  

## 1. API定義が書かれたファイルを自動生成する
### 導入
[apipie\-rails](https://github.com/Apipie/apipie-rails)というgemでAPI定義ファイルを生成できそうだったので、こちらを使いました。

[Getting started](https://github.com/Apipie/apipie-rails#getting-started)に従って、Gemfileへの追加やapipieの初期化を行います。

```ruby:Gemfile
gem "apipie-rails"
```

```
$ bundle install
$ rails g apipie:install
```

`apipie.rb`の生成、`routes.rb`に`apipie`の追加が行われます。

### ドキュメントの記入

[DSL Reference](https://github.com/Apipie/apipie-rails#dsl-reference) を元に、各Controllerにあるメソッドの上部にAPI定義を書きます。
（ここは自作...）

以下は実装例

```ruby:api/v1/users_controller.rb
class Api::V1::UsersController < ApiController

  api :POST, "/api/v1/users/token", "get access token"
  description "ログイン認証をしてトークンを返す"
  formats ["json"]
  param :email, String, desc: "メールアドレス", required: true
  param :password, String, desc: "パスワード", required: true
  returns code: 200, desc: "return user token"
  error code: 401, desc: "Unauthorized"
  example <<-JSON
    {
      detail: "Token Example",
    }
  JSON

  def token
    # 実装略
  end
```

### API定義ファイルを生成

```
$ rails apipie:static_swagger_json
```

`doc/apidoc`に`schema_swagger_form_data.json`が生成されます。


## 2. docker-composeでSwagger UIのコンテナを管理する

### docker-composeの設定

```docker-compose.yml
services:
  app:
   (略)
  
  swagger-ui:
    image: swaggerapi/swagger-ui
    ports:
      - "8081:8080"
    volumes:
      - doc/apidoc/schema_swagger_form_data.json:/swagger.json
    environment:
      SWAGGER_JSON: /swagger.json
```

dockerを起動して localhost:8081にアクセスすると、API定義が読めるようになります。

![スクリーンショット 2020-10-17 17.41.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143745/80eb9cef-2b25-5399-8e8c-665d1092bfe4.png)


参考： [Swagger EditorとSwagger UIをDockerで動かす](http://igatea.hatenablog.com/entry/2018/02/21/120007)

### apipie.rbを調整

プロジェクトに依ってディレクトリ構成やポートが異なるため、必要に応じて`apipie.rb`を調整してください。
こちらも詳細はリファレンス [Swagger\-Specific Configuration Parameters](https://github.com/Apipie/apipie-rails#swagger-specific-configuration-parameters) に載っています。

```ruby:apipie.rb
-  config.api_base_url            = "/api"
+  config.api_base_url            = ""
+  config.swagger_api_host = "localhost:3100"
```

調整後はドキュメントの生成をお忘れなく。


## 3. ローカル環境のSwagger UIで、API定義の閲覧＆リクエスト/レスポンスを行う

現状の設定ではAPIとSwagger UIのコンテナが異なるため、APIリクエスト/レスポンスができません。
解決するためにCORSの設定を行います。

### CORS設定

[rack\-cors](https://github.com/cyu/rack-cors)のgemを追加し、以下のように設定を追加します。

```ruby:Gemfile
gem "rack-cors"
```

```ruby:config/application.rb
module App
  class Application < Rails::Application
    (中略）

    # 以下を追加
    config.x.request = ActiveSupport::InheritableOptions.new(config_for(:request))

    # Permit cross origin
    config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins Rails.application.config.x.request["domain"]
        resource "*",
        headers: :any,
        methods: [:get, :post, :put, :patch, :delete, :options, :head]
      end
    end
  end
end
```

```ruby:config/request.yml
default: &default
  domain:
   - localhost:3100 # appのport
   - localhost:8081 # swagger-uiのport

development:
  <<: *default

test:
  <<: *default

production:
  domain:


```

書き換えたらappのコンテナをrestartをすれば、Swagger UIからリクエストができるようになります。

参考:
 - [Swagger\-UI で TypeError: Failed to fetch が出て死ぬ](https://qiita.com/taito-ITO/items/f4fdfa6a031b91beb080)
 - [CORSの設定を環境ごとに分ける](https://ti-tomo-knowledge.hatenablog.com/entry/2019/07/25/132408#CORS%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92%E7%92%B0%E5%A2%83%E3%81%94%E3%81%A8%E3%81%AB%E5%88%86%E3%81%91%E3%82%8B)