railsのルーティングからOpenAPI(V3)ドキュメントを自動生成・管理するツールを作成し、4ヶ月間会社で運用した話(開発秘話もあるよ)

## はじめに

railsを使ってAPIを開発している方で「 __APIはあるけどドキュメントがない__ 」「 __ドキュメントを書くための時間が確保しにくい__ 」こういう問題を抱えている人に役に立つかもしれない話です。

railsを使ってAPIを開発している会社でドキュメントも一緒に開発している会社って少ないと思います。 __限られたリソースを上手く使ってスケジュール通りに作るために後回しにされる現実がある__ と思います。


この記事で紹介するツールを使えばOpenAPI(V3)形式のAPIドキュメントを書き始めるまでの環境がすぐに整います。
OpenAPIドキュメントの書き方に関してはあんまり言及しません。他の記事を参考ください。


```bash
$ bundle exec rake routes:oas:docs    # ドキュメント生成
$ bundle exec rake routes:oas:ui      # ドキュメント閲覧
$ bundle exec rake routes:oas:editor  # ドキュメント編集
$ bundle exec rake routes:oas:monitor # ドキュメント監視
$ bundle exec rake routes:oas:dist    # ドキュメント配布
$ bundle exec rake routes:oas:clean   # ドキュメント清掃
$ bundle exec rake routes:oas:analyze # ドキュメント分解・分析
$ bundle exec rake routes:oas:deploy  # ドキュメントデプロイ
```

これから紹介するツール<a href="https://yukihirop.github.io/r2-oas/#/" _target="blank" rel="noreopener"> `r2-oas` </a>を使って、約500個のエンドポイントがあるAPIのドキュメントを4ヶ月、6人で書いてきました。(ツール開発は仕事以外でやりました。個人開発です。)

- 目標をどのように立て、どれくらい達成できたのか？
- なぜ目標を達成できなかったのか？
- 複数人でAPIドキュメントを書くには何が重要なのか？
- 開発秘話
- ツールでk8s・moneyforward・leadedeskのドキュメントを分解した話
- ツールの特徴・機能

などが少しでも書けたらいいかなって思います。

![oas_editor](https://user-images.githubusercontent.com/11146767/80364657-4079a100-88c1-11ea-8f56-c9af78829d68.gif)


## 4ヶ月の実績

※会社の許可をとりデータを載せております。

#### 全体の実績

赤線が目標ライン。8/5(月)の週の踊り場はチームで話し合って、全体的な見直しをしようと決めていた日でしたが結局何もしませんでした。

目標は、`25個/週` です。

![全体の実績.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113181/37fff295-a411-2564-8524-45178373f26b.png "全体の実績.png")

#### 各週の実績

8/12(月)・8/19(月)の週に進捗がないのは、夏季休暇を取る人が多かったからです。私自身、8/12(月)の週は夏季休暇取りました。
最初は勢いがあったのですが、後半は僕ともう一人誰かくらいしか書いてくれなかったです。(忙しそうだったのでお願いもしきれませんでした。)

![各週での実績.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113181/a5c77f22-7bf0-aeea-0fa3-56f6fdabc319.png "各週での実績.png")

#### 個人の実績

累積値を表示してます。日付のセルに▲のマークが隅に付いてますが、上期評価の際に振り返るために、何があったか簡単にメモを書いてました。最後の平均値ですが、6/３(月)の週と8/5(月)の週を除いた16週で平均を取ってます。

私は言い出しっぺなので当然目標 `ほぼ` 達成です。(達成じゃないんかい！)

![個別の実績(週表).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113181/de271875-0cb4-30c7-0e66-2569cec1963b.png "個別の実績(週表).png")

#### 進捗管理表

これまで乗せてきたグラフや表はこの進捗管理表から自動生成されてます。
`pathsファイル` というのがrailsで言うところのコントローラーに対応してます。
後で紹介しますが、`r2-oas` は「コントローラー毎にAPIドキュメントが書ける」特徴があります。

![進捗表.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113181/16e1d121-f3c8-2c92-af36-913250043333.png "進捗表.png")

#### レビュー体制

レビューは私が一人で行いました。複数人でレビューしている時間なんてありませんでした。
そんなに厳しく見るのではなく以下の点に注意して見ました。

レビュー修正してもらってる時間はなかったので私がやってました。

- SwaggerUIで開いてエラーにならないか
- SwaggerEditorで開いてエラーにならないか
- タイポしてないか
- レスポンスのschemaやexampleが適切か
- 考えうるクエリパラメーターはちゃんと書いてあるか
- HTTPステータスは間違えてないか
- 一部未完成状態ならsummaryに `[TODO]` が付いているか
- `.paths` ファイルの更新(後から説明します。)

です。最初に雛形を一気に作成するので、ドキュメントの `構造` がしっかりしているかをレビューする必要がなかったです。

#### 進捗報告

こんな感じで毎週月曜日にslackで進捗を報告し、「今週も頑張らなきゃって」思わせるようにしてました。
ワンポイントアドバイスとかも書いてました。メンバーが「今週こそは頑張らねば」って思ったかどうかは知りません。(笑)

![slackでの告知.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113181/4c4d365f-7767-35b3-e903-e041b0e858bb.png "slackでの告知.png")


#### まとめ

チームメンバー6人で4ヶ月APIを書いてみた感想は、最初から感じてましたが普段仕事をしながら優先度が低くなりがちなAPIドキュメントを書いていくのは難しいなと思いました。 私はAPIドキュメントを書きながら、 __ツールの問題点や改善点を解決していくと言う別の楽しみ__ がありましたので続けれましたが、メンバーは違ったかと思います。４ヶ月間文句もあまり言わずに書いてくれたメンバーには感謝しかないです。

一方、APIドキュメントを書いて得られた効果の方ですが、以下のようなものがあったかと思います。

- 軽微な不具合のあるAPIを見つけることが出来た。
- 使われてないAPIを見つけることが出来た。
- 非推奨のAPIにマークをつける事が出来た。
- APIがどんな事が出来るのか全体像が分かるようになった。

まぁ一般的です。

最後に、複数人でAPIドキュメントを開発していく時に必要な事ですが、便利なツールや時間もそうなんですが、一番必要なのは __リーダーの魅力__ なんかなって思いました。「この人が言うなら頑張ろう」「この人のために頑張りたい」そういう人徳がある人がリーダーを務めるべきかなって思いました。プロジェクトXとか下町ロケットとか見て思ってましたが、上手くいくプロジェクトって __リーダーに魅力__ があって、自然に周りの人が動いてるんですよね。(ドラマやバラエティなのでどこまで本当かは分かりませんが、嘘とわかってても引き込まれてしまう感がありますよね。)

まぁこれも一般的な意見ですよね。
どんなにツールを便利しても、使って書いてくれなきゃ意味がないのです。

最後に実績まとめを載っけておきます。

|項目|実績/目標|
|---|----|---|
|チーム合計(個)|217/421|
|チーム平均(個/週)|13.5/25|
|個人合計(個)|77/85|
|個人平均(個/週)|4.8/5|

※進捗管理表から抜け落ちてるエンドポイントがあり全体は約500個くらいあります。


## 簡単なチュートリアル

さて、ここら辺まで読んだらどんな感じのツールか試したくなったでしょう。

モノは試しという事でまずは触ってみましょう。

rails6でAPIドキュメントを作ってみましょう。

SwaggerUIやSwaggerEditorで開いたりする場合は以下の準備が必要です。

```bash
$ brew cask install chromedriver
$ docker pull swaggerapi/swagger-ui:latest
$ docker pull swaggerapi/swagger-editor:latest
```

rails newしたら、Gemfileのdevelopmentに `r2-oas` を追加してください。

```Gemfile
group :development do
 gem 'r2-oas'
end
```

#### 準備

```bash
$ rails _6.0.0_ new example-600 -d mysql --skip-bundle
$ cd example-600
$ bundle install --path vendor/bundle
$ # mysql2のエラーが出るときは以下を実行して、bundle installをやり直す。
$ # bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"
$ #
$ #scaffoldで適当にルーティングを作成する。
$ bundle exec rails g scaffold user name:string age:integer
$ bundle exec rails g scaffold task status:string content:string
$ bundle exec rails g scaffold Api/V1/Account status:string content:string
$ bundle exec rails g scaffold Api/V2/CustomPost status:string content:string
$ # 一旦コミットする
$ git init
$ git add . && git commit -nm "initial commit "
$ # OpenAPI(V3)形式のドキュメントの雛形生成
$ bundle exec rake routes:oas:docs
# ドキュメントはコロコロ変わるのでgitignoreに追加する。
$ echo 'oas_docs/oas_doc.yml' >> .gitignore
$ # 一旦コミットする
$ git add . && git commit -nm "generate docs"
```

#### 生成したドキュメントの編集・表示

```bash
$ # SwaggerEditor(UI)で開く
$ bundle exec rake routes:oas:editor(ui)
$ # SwaggerEditor上で適当に編集して「Ctrl+C」でSwaggerEditorを閉じる。
$ # ファイルに差分が出ることを確認する。
$ #
$ # こんな風にpathsファイル毎に開くこともできます。環境変数のPATHS_FILEを指定します。
$ PATHS_FILE=oas_docs/src/paths/api/v1/task.yml bundle exec rake routes:oas:editor
```

こんな感じの画面が開くと思います。

![Kobito.lwaN6Q.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113181/9c2f480c-b2c4-bfdf-04f0-85df232fd5a6.png "Kobito.lwaN6Q.png")



[SwaggerEditorを操ってるgif](https://gyazo.com/746638303d9874c943e59747a394991e)

こんな感じでファイルが自動生成されている事が確認できるかと思います。

このようにファイル分割する事で、`src`ディレクトリにあるyamlから必要な分だけAPIドキュメントを作成する事を可能にしてます。


```bash
$ tree oas_docs
oas_docs
├── src
│   ├── components
│   │   ├── requestBodies
│   │   │   ├── activestorage
│   │   │   │   ├── direct_upload.yml
│   │   │   │   └── disk.yml
│   │   │   ├── api
│   │   │   │   ├── v1
│   │   │   │   │   └── task.yml
│   │   │   │   └── v2
│   │   │   │       └── post.yml
│   │   │   └── user.yml
│   │   └── schemas
│   │       ├── activestorage
│   │       │   ├── blob.yml
│   │       │   ├── direct_upload.yml
│   │       │   ├── disk.yml
│   │       │   └── representation.yml
│   │       ├── api
│   │       │   ├── v1
│   │       │   │   └── task.yml
│   │       │   └── v2
│   │       │       └── post.yml
│   │       └── user.yml
│   ├── external_docs.yml
│   ├── info.yml
│   ├── openapi.yml
│   ├── paths
│   │   ├── active_storage
│   │   │   ├── blob.yml
│   │   │   ├── direct_upload.yml
│   │   │   ├── disk.yml
│   │   │   └── representation.yml
│   │   ├── api
│   │   │   ├── v1
│   │   │   │   └── task.yml
│   │   │   └── v2
│   │   │       └── post.yml
│   │   └── user.yml
│   ├── servers.yml
│   └── tags.yml
└── oas_doc.yml
```

(※長いので一部省略。)

#### .pathsファイルの役割

.pathsファイルに、pathsファイルのpaths以下の相対パスを書くと、`PATHS_FILE` の環境変数が指定されてない場合は、.pathsファイルに書いた分だけAPIを表示・編集する事ができます。

```bash
$ # .pathsファイルにapi/v1/task.ymlを追加
$ echo 'api/v1/task.yml' >> ~/oas_docs/.paths
$ # SwaggerEditor(UI)で開く
$ bundle exec rake routes:oas:editor(ui)
```

こんな感じのツールです。

どうでしょうか？

使い方がかなり直感的な感じしないでしょうか？

## 開発秘話

`簡単なチュートリアル` をやる事で `r2-oas` の雰囲気は掴めたかと思います。次に開発までの話をしたいと思います。開発までの話は面白いです。どんな問題にぶち当たってどう解決してきたかが分かるからです。

私が現在の会社に転職して最初に任された仕事がrailsで作られたAPIの修正のPRのレビューでした。railsのアクションを見る必要があったのですが、約130行くらい書いてあって、何をやってるのかわからんと思ってメンバーに「APIドキュメントないですか？」と聞いたのが全ての始まりでした。メンバーは「ない。欲しいとは思ってるんだけど...」と答えました。APIドキュメントが無いと聞いて私はがっかりするのかと思ってましたが、内心少し喜んでた気がします。前職で他のチームがSwaggerでAPIドキュメントを書いたとか話を聞くたびに、「いいな！俺もやってみたいな！」って思ってたからだと思います。そんなこんなもあって、私は軽い気持ちで「じゃあ作りましょうよ！」といって世の中のエンジニアがどんなツールを使って開発しているかを調査し始めました。

- `gem` 系では、swagger_blocksやrswagやautodoc、もしgrapeを使ってAPIを作成していたら、grape-swagger 

- `クラウドサービス` 系では、SwaggerHub・Restlet・APIMATIC・Amazon API Gateway

などがありました。

`クラウドサービス` 系は有料で実際使うとなると決裁しないといけなく面倒でした。かといってdslを覚えなきゃいけないしコードを書かないといけないgem系も微妙。dslがないautodocは素晴らしいツールだが、リクエストspecがしっかり書いてある事前提で効力を発揮するものでした。残念ながらリクエストspecは書いてあるがカバレッジは低かった事、それにマークダウン形式ではなくSwaggerUIで実行できるものにしたかったので採用を見送りました。

色々なツールを試しているとある共通の問題にぶち当りました。。どのツールを使っても、 __ゼロからAPIドキュメントを書かなきゃいけない__ という問題でした。エンドポイントは __約500個__ もある。日々色んな仕事をこなしながら __ゼロから書いていくのは途方もなく辛いなと思いました。__

一通りツールを調べて、「ゼロからAPIドキュメントを書くのは辛いですね」みたいな話をしてたら、メンバーが「 __railsなんだからルーティング情報から雛形とかできないかな？__ 」みたいな事を呟きました。私は「これだ！」と思って、その日家に帰ってrailsのルーティング情報をなんとか取得する方法はないか調べました。

Qiitaに `rails routes` で出しているルーティング情報のコードベースでの出し方を紹介している記事を見つけ、それをヒントにコードを読みました。意外と[簡単に取得できる事](https://github.com/yukihirop/r2-oas/blob/master/lib/r2-oas/routing/parser.rb#L59-L91)がわかって、１週間でプロトタイプを作ってみました。

最初に作ったプロトタイプでは、railsのルーティング情報を整形して、OpenAPI形式にしてドキュメントに吐き出すところまでだったと思います。試しにメンバーに見せたら、「SwaggerUIやSwaggerEditorで簡単に開きたいね」みたいな感想だったので(自分も感じてた)、それから1週間くらいかけてSwaggerUIとSwaggerEditorで開けるように作成しました。

ちょうどこの頃、DockerでSwaggerEditorを立ち上げAPIドキュメントを編集する時、そのyamlファイルはローカルストーレジ(5MB)に設定されるという事を知りました。5MBより大きいドキュメントが編集できないのはまずいなって思って、`PATHS_FILE(旧名: UNIT_PATHS_FILE_PATH)` という環境変数を指定して __１コントローラー毎にAPIドキュメントを編集できるよう__ にしました。
(`PATHS_FILE=oas_docs/src/paths/v1/task.yaml bundle exec rake routes:oas:editor` のような使い心地です。)

とりあえず作ったものを会社のコードで試してみたら、いい感じに動いたのでこれはいけると思って、 __このツールを使って書きたい__ と思うようになりました。しかし、他に優先するべき仕事が入ってきて、すぐに書き始めることはありませんでした。

それから1ヶ月半くらい立ってちょうど会社の目標を立てなきゃいけない時期にメンバーとの話し合いで「 __APIドキュメント作成__ 」目標に入れよっかとなりました。そこで初めてメンバーに作成したツールをプレゼンしました。そこから開発がスタートします。最初のツールは `$ref` を再帰的に読み込んでくれない程度のツールでした。

ツールの名前は「 `routes_to_swagger_docs` だよ。」って言ったら、「`routes_to_open_api` の方がよくない？」と指摘されたのですが、ごもっともでした。(最初につけた名前は `routes_to_swagger_docs` でした...長すぎました。)

最初の1週間は「 __どのようなルールを決めて書いていくようにしたら楽なのだろうか？__ 」というのを考えてました。しばらくAPIドキュメント書いていると、HTTPステータス毎にレスポンスが違うのだから、そのレスポンスに対応した `components/schemas` がいるなと感じ始めました。components/shemasは一般的には再利用される前提で使うものですが、微妙にレスポンス形式が違い、再利用するのは混乱の元になるからやめようと思いました。(ざっと見積もって500個〜高々1000個近くはcomponents/schemasが必要でした。)

しかしこのように決意した時にcomponents/schemasの名前をHTTPメソッド・HTTPステータス毎にユニークにつけなければならない問題にぶち当たりました。つまり、

```
GET /v1/tasks/{id} の 200の時のレスポンスを表すcomponents/shemas名をV1_Task_P1_GET_200とする。
```

といった要領で名前をつける必要がありました。
(このルールは実際採用したルールです。`_`はネームスペースを表現したつもりです。)

こんな感じで名前をつけていけば、まぁ名前が衝突することはないだろうって思ってましたが、 __メンバーにルールを守ってもらう__ 事を強要する事になりました。「P1って書いてあるのはpathパラメーターが一個って意味ね」みたいな説明をしてルールブックまで作ってルールを守ってもらうように心がけました。ですが、 __仕上がってくるドキュメントはルールが守られてませんでした。__

名前の統一感が欲しかった私は自分でレビュー修正してでもルールに合致するようにしてました。しかし、やがて限界がきます。「 __なんでルール守ってくれないんだろう__ 」って考えました。たどり着いた結論は「 __ルールがあるのがよくない__ 」でした。つまり、 __components/schemas名も自動で設定できるようにする必要があるなと感じました。__   __もっとドキュメント生成時に動的に処理がしたい。__  __フック__ (before_createとかafter_createとか)の開発に取り掛かりました。

だが、問題にぶつかります。ディレクトリの階層が深いところで、なるべくrailsが適用してくれるメソッド(define_callbacksとか)を使いたくない。「だがどうやってフック作る？」フックなんて自作した事なかったし本当に悩みました。もう諦めて「define_callbackとか使おうかな」っと思いながら、昔vueで作った別のツールを触っていた時、`vuex-orm` の事を思い出しました。`vuex-orm` はvuexでリレーションを持った複雑なデータ構造を上手く扱うためのツールです。(railsで言えば、has_manyとかhas_oneとかを使えるようにしてくれます。)typescriptで書かれてます。自分は、興味を持ってこのツールのドキュメントを過去に読んでました。そこで 「beforeCreateってものがあったな」っていうのを思い出して、実装がどうなっているのかコードを読みました。意外と簡単な仕組みだったのですぐに[パクって実装](https://github.com/yukihirop/r2-oas/tree/master/lib/r2-oas/hooks)しました。これでフックが完成しました。(参考にしたコードは[ここら辺](https://github.com/vuex-orm/vuex-orm/blob/master/src/query/Query.ts#L200-L229))


こんな感じの要領で使えます。

例えば全てのエンドポイントにvalidateというクエリパラメーターを設定したい場合はこう書きます。

```ruby
class RtsdPathItemObject < R2OAS::Schema::V3::PathItemObject
  after_create do |doc, path|
    doc.keys.each do |verb|
      doc[verb]["parameters"] ||= []
      doc[verb]["parameters"].push({
        'name' => 'validate',
        'in' => 'query',
        'description' => 'validationモードか否か'
        'schema' => {
          'type' => 'boolean'
        }
      })
      doc[verb]["parameters"].uniq!
    end
  end
end
```

components/schema名も同じ要領でこうやります。こっちは単にcomponents_schema_nameメソッドのオーバーライドですね。

```ruby
module Components
  class RtsdSchemaObject < R2OAS::Schema::V3::Components::SchemaObject
    # e.x.)
    # GET(200) /v1/tasks/{id} => V1_Task_P1_GET_200
    def components_schema_name(doc, path_component, tag_name, verb, http_status, schema_name)
      path_parameters_count = path_component.path_parameters.count
      excluded_path_parameters = path_component.path_excluded_path_parameters
      excluded_path_parameters_arr = excluded_path_parameters.split("/").delete_if(&:empty?)
      base_schema_name = excluded_path_parameters.split("/").map(&:singularize).map(&:camelize).join("_")
      
      if excluded_path_parameters.eql? "" || excluded_path_parameters_arr.count == 1
        base_schema_name = tag_name.split("/").map(&:singularize).map(&:camelize).join("_") + base_schema_name
      end
      
      if path_parameters_count.zero?
        "#{base_schema_name}_#{verb.upcase}_#{http_status}"
      else
        "#{base_schema_name}_P#{path_parameters_count}_#{verb.upcase}_#{http_status}"
      end
    end
  end
end

```

で最後にこのクラスを使うようにする。


```ruby
R2OAS.configure do |config|
  config.use_object_classes.deep_merge!({
  	  components_schemas_object: Components::RtsdSchemaObject,
  	  path_item_object: Components::RtsdPathItemObject
  }
end
```

このようにOpenAPIドキュメントを生成する時に使用されるクラスを利用者がオーバーライドできるようにしました。

こうやって、components/schemas(requestBodiesも同様)の名前を動的に決まったルールで生成することができ、 __メンバーにルールを守ってもらう必要がほぼなくなりました。__

APIドキュメントを書いていてこのcomponents/schemas名の問題が一番大きかったと思います。後の問題はそんなに大きくなかったです。

- SaggerEditorをブラウザの❌ボタンで閉じてしまって、rubyのプロセスが編集が終わった事を理解できず、ファイルの更新が行われず、編集履歴がぶっ飛んだ。
  
  => 15秒(デフォルト)に1回メモリに保存するようにして回避
  
- EventMachine(SwaggerEditorを開きっぱなしにするために使ってる)とMutexの相性が悪くて大量に出る警告がうざい。(`log writing failed. can't be called from trap context`)

  => rubyのLoggerの中でMutexが使われていたのでrubyのLoggerの実装をパクって[MutexなしのLogger](https://github.com/yukihirop/r2-oas/blob/master/lib/r2-oas/logger/stdout_logger.rb)を作って解決
  
-  SwaggerEditorが貼り付けられたjsonをyamlにコンバートしようとしている時に、15秒に1回のメモリへの保存が走った時に落ちる問題。

　~~=> 不二の病です。直しきれてないです。仮に起きても編集履歴が失われることはないです。~~
　=> `v0.1.3` から治ってます。🎉
　=> `bundle exec rake routes:oas:analyze` というコマンドで解決します。
  
- __急性ペットストア症候群__

　=> これも不二の病です。SwaggerEditorのローカルストレージが上手い事書き換わらずデフォルトで設定されているペットストアのまま立ち上がってしまう問題です。
　=> あんまり続く時はeditorのコンテナを削除すると治ります。
　=> 頻発はしませんが、忘れた頃に起きます。

  
その後は問題なく書き進めました。唯一問題があるとするなら、 __メンバーがなかなかAPIドキュメントを書いてくれなかった事でしょうか？__ この問題だけは最後の最後までそして現在も解決できずにいます。(笑) 私に魅力がないから？（笑)

そうしてあっという間に4ヶ月経ち、現在に至ります。

4ヶ月間安定して使えたこともあって、そろそろ世の中にリリースしたいと思うようになりました。だが、「 __どうやって使えるツールである事をアピールする？__ 」という問題にぶち当たります。最初は「4ヶ月使えたしそのままリリースしちゃえ」って思ってましたが、今思えば甘かったです。(公開されているAPIドキュメントを全然扱えなかったです。components/parametersとかサポートしてなかった。)世の中でよく知られているAPIドキュメントを扱えてこそ役にツールだろうと思って、公開されているAPIドキュメントを探しました。以下の3つのAPIドキュメントを見つけました。変換ツールがあるので、v2かv3かは大した問題ではありませんでした。見つけたドキュメントは全てV2でした。

- [kuberntes](https://github.com/kubernetes/kubernetes/blob/master/api/openapi-spec/swagger.json)(約5.5MBもある事で有名です。行数にしたら約12万行ありました。ぎょえぇぇぇ！)
- [moneyfoward](https://expense.moneyforward.com/api/index.html)(知ってました。)
- [leaddesk](https://leaddesk.com/docs/leaddesk-api/)(たまたま見つけました。)

この3つのAPIドキュメントを分解・分析(analyze)して扱えたら `合格` としよう思いました。
( `OAS_FILE=doc.yaml bundle exec rake routes:oas:analyze` が正常終了するかのテスト)

しかし残念ながら `不合格` でした。(現在は修正して `合格` の状態です。)

以下のリポジトリにドキュメントを分解した結果を置いております。

- [yukihirop/r2oas-k8s-example](https://github.com/yukihirop/r2oas-k8s-example)
- [yukihirop/r2oas-moneyforward-example](https://github.com/yukihirop/r2oas-moneyforward-example)
- [yukihirop/r2oas-leaddesk-example](https://github.com/yukihirop/r2oas-leaddesk-example)

### kubernetesの場合

##### `paths` キーの下に`パス`以外が来る場合が考慮できてなかった。

つまりこういう事

```yaml
paths:
  servers:
  parameters:
```

みたいな場合に扱えなかった問題がありました。OpenAPI(V3)のドキュメントを見るとそのような場合はありました。
なので修正して現在では扱えるようにしました。

##### tagsオブジェクトがない時にエラーになる。

`r2-oas` はtagsオブジェクトがないと使えない問題があります。ですが、kubernetesのAPIドキュメントのようにない場合もあります。tagsオブジェクトは必須ではないので、この問題はツール側で解決する必要を感じました。具体的にはanalyzeする時に、ルートキー(pathsとかcomponentsとか)の階層に `tags` がないなら自動で生成するようにしました。

##### pathパラメーターの定義がないとSwaggerEditorでエラーになる。

SwaggerEditorで開くとpathパラメターがないよとvalidationエラーが起きました。これも頑張ればツール側で吸収しようと思えば出来ましたが、結構大変だったのでやめました。kubernetes側の問題にしました。

![スクリーンショット 2019-10-12 19.14.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113181/5e2791bb-2c83-6bb8-5e91-73ac136116df.png "スクリーンショット 2019-10-12 19.14.30.png")


##### $refの再帰的読み込みの際にstack too deepが起こるケースがあり得た。

kubernetesのcomponents/schemasにはJSONSchemaが使ってあることが分かりました。こんな感じで書いてあって見事に無限ループに陥りました。`JSONSchemaProps` が `JSONSchemaProps` を参照してます。

現在では修正済みです。

```json
"io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps": {
                "description": "JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/).",
                "properties": {
                    "$ref": {
                        "type": "string"
                    },
                    "$schema": {
                        "type": "string"
                    },
                    "additionalItems": {
                        "$ref": "#/components/schemas/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrBool"
                    },
                    "additionalProperties": {
                        "$ref": "#/components/schemas/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrBool"
                    },
                    "allOf": {
                        "items": {
                            "$ref": "#/components/schemas/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps"
                        },
                        "type": "array"
                    },
```

##### components/securitySchemes が扱えなかった。

SwaggerUIとかの `authorize` ボタンの部分を作り出す役割があるパーツです。
当然、サポートしました。

##### components/schemas名とかに `.` が使ってあった。

`.` を使ってネームスペースを表現してありました。つまりこんな感じ

`io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaPropsOrBool`

これは `_` より短くかけていいなって思って即サポートするようにしました。

### moneyfowardの場合

##### components/parametersが扱えなかった。

この出来事をきっかけに、全てのcomponentsオブジェクトをサポートしました。
ですが、以下の5つのオブジェクトのサポートは試験的です。

- responses
- examples
- headers
- links
- callbacks


##### V3でサポートしてない型でエラー

V2ではサポートしていたがV3でサポートされてない `type` があるというエラーなどが出ました。


![スクリーンショット 2019-10-12 18.47.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113181/bd8d446f-7fe5-85ca-879a-0abfd2f6cbdf.png "スクリーンショット 2019-10-12 18.47.40.png")


yamlファイルで管理しているので適宜修正すればいいだけなので特に何もしませんでした。
型変換までサポートするとツールが複雑になるのでサポートしませんでした。

### leaddeskの場合

##### pathパラメーターの定義がないとSwaggerEditorでエラーになる。

SwaggerEditorで開くとpathパラメターがないよとvalidationエラーが起きました。これも頑張ればツール側で吸収しようと思えば出来ましたが、結構大変だったのでやめました。leaddesk側の問題にしました。

##### V3でサポートしてない型でエラー

V2ではサポートしていたがV3でサポートされてない `type` があるというエラーなどが出ました。

![Kobito.CObhFa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113181/1803b7b8-5dd3-eb37-1a5e-c0e8b96772ba.png "Kobito.CObhFa.png")



yamlファイルで管理しているので適宜修正すればいいだけなので特に何もしませんでした。
型変換までサポートするとツールが複雑になるのでサポートしませんでした。

### まとめ

このように世の中で公開されているAPIドキュメントを実際に分解・分析できる事を確認しております。
kubernetesのAPIドキュメントが分解・分析出来た時は感動がでかかったです！

## r2-oasの特徴

簡単にツールの特徴をまとめておきます。

- すでに作成されたAPIのドキュメント作成に便利なツールである。
- 覚えるのが辛いDSL形式を書かなくていい。
- 複数人で開発してもコンフリクトはおきない。(.pathsファイルはおきる。)
- 簡単に使う分では `設定なし` で使える。
- 高度な設定以外は、誰でもすぐ使える。
- yamlファイルで管理できる。(拡張子は `.yml`)
- ツールが合わないと思った時に簡単に他のツールに移れる。
- 逆に他のツールからの移行も簡単にできる。
- SwaggerEditor上での編集結果がローカルに反映される。

## r2-oasの機能

簡単にツールの機能をまとめておきます。

- 雛形を一気に作成できる。
- ターミナルからサクッとSwaggerEditor(UI)を開いて書ける(見れる)。
- 編集が重くならないように、コントローラー毎編集できる。
- ドキュメント生成の際、フック処理で共通のクエリパラメーターをもたせたりなど自由度の高い書き方ができる。
- 必要なコントローラーの分だけ指定してドキュメントを作成する事ができる。
- `components/schemas(requestBodies)` の名前などを規則的に生成できる。
- `components/schemas(requestBodies)` の __擬似ネームスペース__ をサポートしている。
	- namespace1.namespace2.Model (namespace_typeが `:dot` の時)
	- Namespace1_Namespace2_Model (namespace_typeが `:underbar` の時)

詳しくは[yukihirop/r2-oas](https://github.com/yukihirop/r2-oas/blob/master/README.ja.md)を見てください。

## より高度にr2-oasを使う例

`bundle exec rake routes:oas:docs` でドキュメントを生成する時に

- 共通のクエリパラメーターのvalidateを持たせる
- 動的にcomponents/schemas(requestBodies)名を規則的に決定する

例を用意しております。

詳しくは [yukihirop/r2oas-advanced-example](https://github.com/yukihirop/r2oas-advanced-example)

## ツールの名前に関して

このツールは __railsのルーティング情報からSwaggerドキュメントを生成するツール__ だから、「 __routes_to_swagger_docs__ 」としていたのですが、サポートしているのはOpenAPI(V3)形式のドキュメントだけだし、モジュールを書くときに「`RoutesToSwaggerDocs`」は長いなぁと感じるようになりました。そこで短縮する事を考えて「r2sd」とか「rtsd」とかを頭に浮かべましたが、OpenAPISpecificationを「OAS」と省略する文化がある事を知ったので、

- r2oas
- r2-oas
- rtoas

に候補を絞りました。

でここからが重要なんですが、「r2」と聞くと「d2」じゃないですか！「R2-D2」

真ん中の候補を選べばスターウォーズに出てきそうな感あってなんか良さそうだなって事で「 __r2-oas(R2-OAS)__ 」になりました。

以上。

## おわりに

私がこのようにツールを作成し、APIドキュメントを書いてこれたのは会社の理解があったからだと思います。
入社して間もない私のアイディアを採用し、私の試みに十分に時間を割いてくれた。おかげで楽しみながら問題解決ができ、プログラマーとしても実力が上がったような気がします。もしアイディアを採用してもらえず実験環境がなかったら、ツールを磨く事なく「 __現実ってこんなもんだよな__ 」って気を落としていた思います。このような記事を書くこともなかったでしょう。本当に感謝しかありません。
(何かしらのツールでAPIドキュメントを書いてたとは思いますが...)

話は変わって下期が始まりましたね。

最近、部長と上期評価面談があって「目標高すぎたね(笑)」って言われました。ワンチャン行けるかなって思ってたけど結果的に、`25個/週` は非常に高い目標でした。下期は上期の実績(`13.5個/週`)を参考に `10個/週` と目標を設定しました。

下期こそは目標を達成したいと思っております。
この結果は3月の終わりにQiitaで報告しようと思ってます。

お楽しみに！