Swagger でソースコードのアノテーションからRESTなAPIドキュメントを自動生成する

# swaggerとは

"API Blueprint"、"RAML"と並ぶAPIドキュメントテーションツール。
RESTful APIの記述標準化を目指す"Open API Initiative"や"Amazon API Gateway"などでも採用されている。

* jsonより書きやすいyaml形式で記述できるwebエディタがある  
  ⇒ [Swagger Editor](http://editor.swagger.io/)
* ソースコードに記述したアノテーションからjson出力することができる  
  ⇒ PHPでは[swagger-php](https://github.com/zircote/swagger-php)
* swaggerはjson形式（実際にはyamlで書いてjsonで出力）でAPI定義記述するもので、作成したjsonを元にブラウザで参照・実行できるツールがある  
  ⇒ [swagger-ui](https://github.com/swagger-api/swagger-ui)
* swaggerのjsonからAPIのコードを生成することができる  
⇒ [swagger-codegen](https://github.com/swagger-api/swagger-codegen)

今回は、swagger-phpを使用してアノテーションからjsonを生成し、swagger-uiから参照するという流れを試してみる。

# 前提

* mac OSX 10.13
* nginx + php-fpm + php5.6(FuelPHP) on docker

# swagger-php

PHPソース上のアノテーションからswaggerドキュメントを作成してくれるライブラリ。

### インストール

composerでインストールする

```
    "require-dev": {
    	"zircote/swagger-php": "*",
    }
```

### アノテーション記法

包括的なドキュメントがないような。。。

* [Getting started](https://github.com/zircote/swagger-php/blob/master/docs/Getting-started.md)
  * とりあえず雰囲気はこんな感じ
* [swagger-php/Examples](https://github.com/zircote/swagger-php/tree/master/Examples)
  * 上記のサンプルは2.x系で書かれているので参考になる
* [swagger-explained](http://bfanger.github.io/swagger-explained/)
  * 画面左側に表示されているJSONの各項目にカーソルを合わせると、その項目についての説明を表示してくれる
  * 対応するアノテーションも表示してくれる。
* [OPENAPI SPECIFICATION(fka Swagger RESTful API Documentation Specification)](http://swagger.io/specification/)
    * swagger2.0のマニュアル
* [Swagger-php 1.x documentation](http://zircote.com/swagger-php/1.x/)
    * swagger1.x系の記法なので、json出力時に`@Resource`や`@Api`の箇所で deprecated が発生した
    * swagger-phpで1.x系に対応させる方法は未確認

（追記）
アノテーション記法について書きました
⇒ [Swagger-PHPでSwagger2.0に対応したアノテーションサンプル](http://qiita.com/teratsyk/items/795412717766f4827897)


### JSON出力

composerで入れたswaggerを実行する

```
$ fuel/vendor/bin/swagger fuel/app/classes/controller
```

カレントディレクトリに`swagger.json`が出力される。
`-o`オプションで出力先を変更可能。

# swagger-ui

### インストール(docker)

```
$ git clone https://github.com/swagger-api/swagger-ui.git
$ cd swagger-ui
$ git checkout --force refs/tags/v2.1.4
```

dockerでビルドできるらしいので試してみたが、`npm install`の依存性warningのせい？でエラーになってしまった。
しょうがないのでローカルにインストールした。
一応dockerで試した手順も記載しておく（[docker-toolbox](https://www.docker.com/products/docker-toolbox)を使用）

```
$ docker-machine create --driver=virtualbox swagger
$ docker-machine env swagger
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.101:2376"
export DOCKER_CERT_PATH="/Users/hoge/.docker/machine/machines/swagger"
export DOCKER_MACHINE_NAME="swagger"
# Run this command to configure your shell:
# eval $(docker-machine env swagger)
$ eval $(docker-machine env swagger)
$ docker build -t swagger-ui-builder .
```

npm installでワーニングが出てしまったので、警告を無視するようにdockerfileを編集してみる。
※macローカルで、`npm install`でワーニングが出たが、swagger-uiが動作することは確認済み.

```shell-session:swagger-ui/Dockerfile
RUN     npm install
  ↓
RUN     npm install | true
```

再度 docker build 実行

```
$ docker build -t swagger-ui-builder .
npm http 200 https://registry.npmjs.org/express
npm http GET https://registry.npmjs.org/express/-/express-4.13.4.tgz
npm http 200 https://registry.npmjs.org/express/-/express-4.13.4.tgz
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR!     /build/npm-debug.log
npm ERR! not ok code 0
 ---> 43f85ad5f9da

Removing intermediate container c3ff9fac51d6
Step 9 : ADD . /build
 ---> c475377473f7
Removing intermediate container e91f262ad758
Step 10 : CMD ./node_modules/gulp/bin/gulp.js serve
 ---> Running in 9ed882094940
 ---> dd2230ad6a05
Removing intermediate container 9ed882094940
Successfully built dd2230ad6a05
```

`npm install`ではエラーが出力されているが通った。最後の起動は成功しているっぽい。
ただし、次のコンテナ起動がコケてダメだった。。。

```
$ docker run -p 127.0.0.1:8080:8080 swagger-ui-builder
/bin/sh: 1: ./node_modules/gulp/bin/gulp.js: not found
```

よく分からないのでローカルにインストールした。

（追記）
docker-composeでコンテナ化しました。
⇒ [swagger-ui をdockerで使う](http://qiita.com/teratsyk/items/a58abfeaa281ae0ff702)


### インストール(macローカル)

```
$ npm install
npm WARN bl@0.7.0 requires a peer of stream-browserify@* but none was installed.
$ npm run build
```

insatllで１件警告が出たけど、とりあえずbuild通った。


### サーバ起動

```
$ npm run serve
```

ブラウザで `http://localhost:8080` にアクセス。

### ドキュメント参照

画面上部のテキストボックス（デフォルトでは`http://petstore.swagger.io/v2/swagger.json`と表示されている）に、対象のjsonへのURLを入力して "Explore"をクリックすればswaggerで記載した内容が表示される。

つまり、読み込む対象のjsonはhttp経由で指定する必要がある。。
ローカルファイルを直接見れないっぽい。。。

なので、先ほど生成したswagger.jsonをドキュメントルートにおいて、`http://192.168.99.100:81/swagger.json`という感じで取得しようとしたが、下記のエラーが発生。

> Can't read from server. It may not have the appropriate access-control-origin settings.

今回はAPIをVM上に作成していたためCORSエラーが発生してしまった。。。

これについては、とりあえずnginxに下記の設定を行って回避した。
(後に実際APIを叩く際にもCORS対応が必要なのでphp用のロケーションにも設定しておく)

```shell-session:/etc/nginx/conf.d/default.conf
    location / {
    	...
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS";
        add_header Access-Control-Allow-Headers "Content-Type, Origin, Authorization, Accept";
        add_header Access-Control-Allow-Credentials true;
        ...
    }
    ...
    location ~ \.php$ {
    	...
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS";
        add_header Access-Control-Allow-Headers "Content-Type, Origin, Authorization, Accept";
        add_header Access-Control-Allow-Credentials true;
        ...
    }
```

これで、とりあえずjsonの内容がブラウザに表示された。

# swagger-uiからAPIの実行

各エンドポイントにある`Try it out!`ボタンで、実際にAPIを実行することができる。

ただし、メソッドが`GET`以外の場合、一度`OPTIONS`メソッドで疎通確認した後に本来のメソッドで実行するというよく分からない挙動をする。。。
初回の`OPTIONS`メソッドによるアクセスについては200系の正常レスポンスを返してあげないと正しく動かないので、`OPTIONS`メソッドでアクセスがきたらOKを返すようにする必要がある。

とりあえずnginxの設定で200を返すようにしてみたら、`OPTIONS`メソッドのアクセスだけで終わってしまった。。

```shell-session:/etc/nginx/conf.d/default.conf
if ($request_method = OPTIONS) {  
    return 200;
}
```

なので、API側で`OPTIONS`メソッドだったら処理終了させるようにしたら、その後の`POST`も動いた。
nginxとAPIを別サーバにしているせいかもしれないが、よく分からない。。。


# bootprint-swagger

swagger-uiだと、jsonからhtmlを作成しているのでnode上でないと参照できない。。
静的なhtmlが欲しい場合は、変換ツールを使用する。

[bootprint-swagger](https://www.npmjs.com/package/bootprint-swagger)

### インストール

```
$ npm install -g bootprint
$ npm install -g bootprint-swagger
```

### 実行

```
$ mkdir swagger-html
$ bootprint swagger http://192.168.99.100:81/swagger.json swagger-html
Loading bootprint-swagger 0.13.1
Loading bootprint-json-schema 0.8.6
Loading bootprint-base 0.7.2
[ 'swagger-html/index.html',
  'swagger-html/main.css',
  'swagger-html/main.css.map' ]
```

出力を一つのファイルにまとめたい場合は、別途`html-inline`を使う。

```
$ npm -g install html-inline
$ html-inline swagger-html/index.html
```

# その他

[swagger-to-raml](https://github.com/mulesoft/swagger-to-raml-object)とかもあるので、RAMLにしてから[raml2html](https://github.com/raml2html/raml2html)した方が見やすいHTMLになる気はする。。