10分くらいでなんとなくわかるSwagger 

ある程度の規模を想定した、Swaggerで具体的に内容を書いていけるようになるまでの概要と構成についてのまとめ。
具体的な書き方はあまり難しくないが簡単でもないため、そこそこ学習コストかけて慣れるしかない。公式のドキュメントが割としっかりしていてサンプルも充実しているからそこまで困らないように感じている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116209/71fd86ab-71f8-0ad8-06a6-af9c56afbff8.png)

# What's Swagger
若干ややこしいが大きく2つの意味がある。

1. JSON or YAMLで記載する「RESTful APIドキュメントの標準規格」**のバージョン2.xの頃の名称**
    * 3.0からは**OpenAPI**（OpenAPI Specification）という名称になっている
    * 「Open API Initiative」という団体が推進している
    * いくつかある標準規格の中で世界標準になりつつある
2. Swagger or OpenAPIのフォーマットでRESTful APIドキュメントを作成するための**ツール群**
    * `Swagger UI`: 標準フォーマットを良い感じにHTML形式に変換してブラウザ上で表示するためのViewer
        * 画面上からパラメータを指定してAPIを実行することも可能
    * `Swagger Editer`: 上記のUIとセットになっていてリアルタイムに描画を確認しながらオンライン上で編集できるエディタ
        * 補完やバリデーションチェックもしてくれて優秀
        * 当然だが、別にこれを使わなくても書ける
    * `Swagger Codegen`: 標準フォーマットの定義に則った動作をするAPIサーバと各種言語用のAPI実行コードを生成してくれるらしい
        * 試していなくて実用的な使い方の情報も見当たらなかったためよくわからない
    * 他にもあるようだが上記の3つが代表的なツール
        * いずれもSwagger公式からDockerイメージが提供されていて簡単にオフラインで稼働させられる
        * https://hub.docker.com/u/swaggerapi

# Sample (YAML)

https://editor.swagger.io
上記のURL（Swagger Editor）に下記のYAMLをコピペするとSwagger UIで見れる。
公式のサンプルからだいぶ削ったシンプルなサンプル。

```yaml
openapi: 3.0.0
info:
  title: Swagger Petstore
  version: 1.0.0
servers:
  - url: http://192.168.123.45/v2
tags:
  - name: user
paths:
  /user/{username}:
    get:
      tags:
      - user
      summary: Get user by user name
      parameters:
      - name: username
        in: path
        description: 'The name that needs to be fetched. Use user1 for testing. '
        required: true
        schema:
          type: string
      responses:
        200:
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
    put:
      tags:
      - user
      summary: Updated user
      parameters:
      - name: username
        in: path
        description: name that need to be updated
        required: true
        schema:
          type: string
      requestBody:
        description: Updated user object
        content:
          '*/*':
            schema:
              $ref: '#/components/schemas/User'
        required: true
      responses:
        400:
          description: Invalid user supplied
          content: {}
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
          format: int64
        username:
          type: string
```

# Small Practice

実行するための最小構成
`servers`は指定しないと`localhost`になるため、APIサーバとSwagger UIを動かす場所が同じなら省略可

```yaml
openapi: 3.0.0  # Swagger or OpenAPIのバージョンの指定（必須）
info:
  title: Swagger Petstore  # ページタイトル（必須）
  version: 1.0.0  # リリースのバージョン（必須）
servers:
  - url: http://192.168.123.45/v2  # APIサーバのURL（無くてもエラー出ないがほぼ必須）
paths:  # 各種APIの仕様を記載していく最もメインとなる部分
  /hoge:  # パス
    get:  # メソッド
      responses:  # 1つ以上のステータスコードとdescriptionが必須
        200:
          description: hogehoge
```

ただし、実際は`paths`にすべて記載していくと肥大化して編集や管理が大変になるため、具体的な内容は`components`に外出しして`paths`の方では参照させるだけにするのが一般的な様子。

```yaml
openapi: 3.0.0 
info:
  title: Swagger Petstore
  version: 1.0.0
servers:
  - url: http://192.168.123.45/v2
paths:
  /hoge:
    get:
      responses:
        200:
          # componentsを参照させる
          $ref: '#/components/responses/GenericSuccessResponse'

# 以下を追加（実際はパラメータ定義やリターンの定義なども書いていくので結構長くなる）
components:
  responses:
    GenericSuccessResponse:
      description: hogehoge
```

# Divide Files

**※公式にはファイル分割管理のベストプラクティスは特に書かれていません（たぶん）**
そのため以下は公式の仕様やネットの情報を見ながら試行錯誤して考えた方法になります。  

`components`に外出ししていってもAPIの数だけ`paths`は増えていくし、使い回せない部分も多いので1つのAPIに対してそれなりの量の`components`を書いていくことになり、すぐにファイルが肥大化してしまう問題が発生する。公式のPetstoreのサンプルですら700行を超えている。
そこで、`components`の参照で使用した`$ref`は外部ファイルを参照することも可能であることを利用し、ファイルの分割管理を行う。
ただし、分割してしまうと1ファイルではなくなるためSwagger Editorで編集することができなくなるデメリットがある。
どちらが良いかは規模による。

```bash
# ディレクトリ構成
./
├─ index.yaml     # Swagger UIで見るファイル
├─ paths/
│   └─ hoge.yaml  # /hogeの仕様書の雛形となるファイル
└─ components/
    └─ hoge.yaml  # /hogeの具体的な仕様を定義したファイル
```

```yaml
# ./index.yaml
openapi: 3.0.0
info:
  title: Swagger Petstore
  version: 1.0.0
servers:
  - url: http://192.168.123.45/v2
paths:
  /hoge:
    $ref: './paths/hoge.yaml'
```

```yaml
# ./paths/hoge.yaml
get:
  responses:
    200:
      # 「./paths/hoge.yaml」から見た相対パスになることに注意
      $ref: '../components/hoge.yaml#/responses/GenericSuccessResponse'
```

```yaml
# ./components/hoge.yaml
responses:
  GenericSuccessResponse:
    description: hogehoge
```

しっかり作成すると`components`が肥大化してしまうおそれがあるが、その場合は更に細分化することを検討した方がいいかもしれない。
また、この場合は複数のAPIで`components`を再利用するとどこで何を使っているのかが複雑になってしまうため、数ヶ所でしか利用しない場合は再利用せずにコピペしたり、多くの場所で再利用するものは`common.yaml`や`tags_XXX.yaml`のような抽象的な名前のファイルに定義するなどの設計にするのがよいと思う。  

ちなみに`info`や`paths`をまとめて別ファイルにして参照することはできない。
これは明示的に公式にも書かれている仕様。
https://swagger.io/docs/specification/using-ref/

```yaml
# Incorrect!
info:
  $ref: info.yaml
paths:
  $ref: paths.yaml
```

## Divide Files -another version-

要は複数のYAMLから1つのYAMLを生成すればよいので、ファイル分割のアイデアはいろいろ考えられる。
ただ、少なくとも現状では上記の方法で実現するのが最も無難だと思う。

* 分割したSwaggerのファイルを1つのファイルにマージするツールを使う
    * いずれもローカルでマージコマンドを実行する必要あり
    * 編集してから確認までの手間がかかって煩雑になってしまう問題がある
* システム構成やコード作り込み頑張ってマージ自動化やリアルタイム生成する
    * CI回したり、各種YAMLを読み込んでマージして返すAPIを作ってUIでそのAPIのURLを参照させれば実現は可能
    * 本当に必要なら検討してもよいが、複雑になりがちなのでなるべく避けたい

# Link

参考にしたリンク（主にQiita）

- [Swaggerの概要をまとめてみた。](https://qiita.com/gcyata/items/342073fa7607fd4082bd)
- [OpenAPI (Swagger) 超入門](https://qiita.com/teinen_qiita/items/e440ca7b1b52ec918f1b)
- [Dockerを使ってswagger.yamlをサクッとみる (swagger-ui 3.x)](https://qiita.com/NewGyu/items/6a00e0f09a7f8a93cfd5)
- [Swaggerで作るSPECファイルを小さいファイルに分割する](https://www.hands-lab.com/tech/entry/913.html)
- [Swaggerをファイル分割しながら、編集できるものを作成しました。](https://blog.ryou103.com/post/swagger-multi-file-in-docker/)