こんにちは、サーバーサイドグループの五十嵐です。
今年の4月に弊社に転職して初めて技術ブログを書きます。
今回はAPIドキュメントの作成ツールとしてGemrspec-openapi
を導入した話について書いてみます。
弊社では以前からOpenAPIを導入しドキュメント管理を行なってきました。
しかし、OpenAPIを導入する前からあったAPIに対するドキュメントは不足していたり内容が不十分なものもありました。
このようなツールは導入当初はモチベーションが高くても、年月が経つ内にモチベーションが低下したり、メンバーが変わることで存在自体なかったことになったりはよくあることだと思います。
実際にOpenAPIは全て手で書こうとするとはっきり言って非常にめんどうで、そこそこ工数もかかります。
API新規作成時は自分で必要な情報がわかっているので書きやすいですが、既存のAPIで新しくドキュメントを作成しようとするとかなりの負担です。
そこで、ドキュメント作成の負担をなるべく下げるためにGemrspec-openapi
を導入してみることになりました。
rspec-openapiの導入
rspec-openapiはrspecのRequest SpecからOpenAPIを生成するGemで使い方も非常に簡単です。
インストール
まずはGemをインストールします。
test
でしか使わないのでgroup :test
に追加しましょう。
group :test do gem "rspec-openapi" end
実行
以下のように作成したいAPIに対応するRequest Specを指定して実行します。
普段のspec実行の際に頭にOPENAPI=1
と入れるだけでspec実行後にOpenAPIを生成します。
OPENAPI=1 rspec spec/requests/hoge_spec.rb
もちろん1つずつの実行でなくても問題なく実行できます。
OPENAPI=1 rspec spec/requests/
Open APIの確認
実行するとリクエスト情報とレスポンス情報を元にOpenAPIを生成してくれます。
デフォルトではdoc/openapi.yaml
に作成されます。
キー情報や型情報だけでなく、実際のレスポンスを元にExampleも生成してくれます。
openapi: 3.0.3 info: title: rspec-openapi paths: "/tables": get: summary: index tags: - Table parameters: - name: page in: query schema: type: integer example: 1 - name: per in: query schema: type: integer example: 10 responses: '200': description: returns a list of tables content: application/json: schema: type: array items: type: object properties: id: type: integer name: type: string # ...
これはサンプルでほんの一部ですがこれを各APIそれぞれ手作業で書いていく・・・
今考えると寒気がします。
rspec-openapi
を触ってみて
触ってみての第一印象は「何これ!?便利すぎ!!」でした。
API作成時必ず書くRequest Specから作成してくれるとはなんて素敵なのでしょう!!
今まで既存のAPIのドキュメント作成時はソースを読んでレスポンスを追い、「これとこれがこう言う形で返るよな・・・サンプル値は・・・」と手作業で書いていましたが、rspec-openapi
はrspec実行時に頭にOPENAPI=1
とつけるだけです。
これに感動しない人はいるでしょうか?いや、いないはずです。
こんなにも便利で素晴らしいGemですが、触ってみていくつか注意点もありました。
注意点
specをしっかり書いていないと期待するクオリティーのOpenAPIが生成されない
rspec-openapiはテストケースとして書いたspecを実行しリクエストのパラメータにしたりレスポンスのサンプル値にします。
よってテストをしっかり書いておかないと、必須ではないパラメータやレスポンスの記載が漏れてしまうことがあります。必要のないrequiredが付与される場合がある
specの実行結果からOpenAPIが作られる都合上、どれが必須なのかは判断仕切れないと思われます。
テスト時に値があるものにはrequiredがつく傾向があり、テストではparamsを設定したが実際は必須ではない場合はOpenAPI作成後に手動で削除する必要がある場合があります。ステータスコードが重複するテストケースはどれか1つから生成される
レスポンスのステータスコードが重複するテストケースがある場合、このテストケースでOpenAPIを作成する!という指定はできません。
指定したい場合は以下のようにテストケースを指定してあげることで解決可能です。
OPENAPI=1 rspec spec/requests/hoge_spec.rb:10
不要になったエンドポイントやキーの削除、レスポンス結果の更新が行われない
実装上、一度生成するとキーが同じなら上書きされません。
よって後から手動で修正しても修正したものが残りますが、doc/openapi.yaml
がある状態でspecを修正して再実行したとしても、不要になったエンドポイントやキーの削除、レスポンス結果の更新が行われません。
一旦既存のファイルを削除することで再作成されますが、手直しで修正した部分も消えてしまうので注意が必要です。$ref
を使った重複schemaなどの参照は手作業で行う必要がある
自動作成時はschemaが重複した場合も冗長に生成されてしまいます。
しかし、手動で$ref
を使うように修正することで再実行時にはcomponents/schema
を使ってくれます。- 以下例
一度OpenAPIを作成します。
以下の場合、responseのUserが重複しています。
paths: "/users": get: responses: '200': content: application/json: schema: type: array items: User: type: object properties: id: type: string name: type: string role: type: array items: type: string "/users/{id}": get: responses: '200': content: application/json: schema: User: type: object properties: id: type: string name: type: string role: type: array items: type: string
生成されたOpenAPIを以下のように$ref
を使うように修正します。
この時、components/schema/User
は自分で書いておく必要はありません。
paths: "/users": get: responses: '200': content: application/json: schema: type: array items: $ref: "#/components/schemas/User" "/users/{id}": get: responses: '200': content: application/json: schema: $ref: "#/components/schemas/User"
この状態で再実行します。
以下のようにcomponents/schemas/User
が自動生成されます。
paths: "/users": get: responses: '200': content: application/json: schema: type: array items: $ref: "#/components/schemas/User" "/users/{id}": get: responses: '200': content: application/json: schema: $ref: "#/components/schemas/User" components: schemas: User: type: object properties: id: type: string name: type: string role: type: array items: type: string
このように$ref
を使うことで重複箇所を最小化できます。
まとめ
私たちのグループではrspec-openapi
は、OpenAPIの土台作りには非常に便利で、そこから軽く手直しをする程度で運用できると結論づけて運用を開始しました。
このおかげで現在はほぼ全てのAPIがドキュメント化され、最新の状態になってきています。
便利なGemを使わせていただき、モチベーションを高く保ち続けることで、しっかりとAPIドキュメント管理を行なっていきたいと思います。