Myuon Corp. Advent Calendar 2016 3日目です。
Swagger を vagrant で建てた CentOS6.5 上で動かしてみた
Swagger を業務で使ってみようということになり、Vagrant 上に導入してみました。
Vagrant で CentOS6.5 を建てる方法については、割愛して Swagger とはなにか、導入の仕方と動かし方、扱い方をまとめてみようと思います。
Swagger とはなにか
Swagger とは Restful な API 仕様の記法と、記述するためのツール群です。
API 仕様を yaml か json で記述することができます。
何より特徴的なのが yaml, json の編集をブラウザ上で実行する事ができ、ライブプレビューで仕様書風なページが表示されることです。
また、記述したものをプレビューされている仕様書風なページ上で実行することができます。
実際に作成した仕様書を元に Server, Client のソースコードをジェネレートすることができるみたいですが、そこはまだ触れていないのでいずれまとめたいと思います。
建てた Vagrant の環境
今回、Swagger の Editor を Vagrant で建てようということになり、ミニマムな状態の CentOS (6.5) を建てました。
-
OS
$ cat /etc/redhat-release
CentOS release 6.5 (Final)
$ uname -a
Linux swagger 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 03:15:09 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
* 容量
```
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 7.3G 1.6G 5.4G 24% /
tmpfs 295M 0 295M 0% /dev/shm
vagrant 465G 370G 96G 80% /vagrant
Swagger の導入
Swagger には node.js 製のもの、 javascript 製のものがあるようです。
まずは、エディタを動かしたいので swagger-editor を導入します。
必要なものは以下のものです。
- nodejs
- npm
- wget
- http-server
- swagger-editor
上の3つは yum でインストールしちゃいます。
$ sudo yum install -y nodejs
$ sudo yum install -y npm
$ sudo yum install -y wget
次の、 http-server は node.js 製の HTTP サーバです。
npm でインストールします。
$ npm install -g http-server
単に swagger-editor 内の index.html が HTTP サーバ上で動作すれば良いので、手元に rails があれば public の下に swagger-editor を置いても動作します。
とりあえず動かすのが目的であれば、他のもので代用しても問題ありません。
最後に、swagger-editor の取得です。
wget でソースコードの zip をダウンロードします。
$ wget https://github.com/swagger-api/swagger-editor/releases/download/v2.10.4/swagger-editor.zip
ダウンロードしたものは解凍しましょう。
$ unzip ./swagger-editor.zip
Swagger の動かし方
ようやく、swagger-editor を動かす時が来ました。
http-server swagger-editor
実行すると、次のような出力が得られます。
Starting up http-server, serving swagger-editor
Available on:
http://127.0.0.1:8081
http://10.0.2.15:8081
http://192.168.33.52:8081
Hit CTRL-C to stop the server
ブラウザでアクセスすると見事エディタが起動します。
導入はすごく簡単ですね。
Swagger の扱い方
Swagger の扱い方として、次のものを順にまとめます。
- 書き方
- Type
- プロジェクト
書き方
swagger-editor を起動してアクセスすると、まずエディタにはサンプルの yaml が貼り付けられていて、おおよその書き方はそのサンプルのコメントで分かるようになっています。
また、エディタのステータスバーの File
> Open Example...
から別のサンプルを開くこともできるので、不足分はそちらで補うこともできそうです。
書き方の解説は yaml 形式で、例に上げるサンプルを petstore_full.yaml
としてまとめます。
-
構成
ルート階層は次の表のようになっています。
key 型 用途 swagger string swagger のバージョン info object API の情報 host string API のホスト basePath string API のベースパス schemes array 使用可能なスキームのリスト paths object API のパスのリスト securityDefinitions: object 認証などのセキュリティの定義 definitions object モデルのリスト それぞれの解説をします。
swagger はそのまんまですね。
サンプルでは次のようになっています。
swagger: "2.0"
---
**info** は次のような情報を持ちます。
| key | 型 | 用途 |
|:--- |:--- |:--- |
| description | string | API がどういうものなのかの説明文 |
| version | string | API のバージョン |
| title | string | API のタイトル (プロジェクト名) |
| termsOfService | string | 利用規約ページ |
| contact | object | `name` をキーとした連絡先 |
| license | object | `name` をキーとしたライセンス名、 `url` をキーとしたライセンス条文のページのリンク |
ミニマムで設定するのであれば、 `description` , `title` , `version` だけで良さそうです。
サンプルでは次のようになっています。
```
info:
description: |
This is a sample server Petstore server.
[Learn about Swagger](http://swagger.io) or join the IRC channel `#swagger` on irc.freenode.net.
For this sample, you can use the api key `special-key` to test the authorization filters
version: "1.0.0"
title: Swagger Petstore
termsOfService: http://helloreverb.com/terms/
contact:
name: apiteam@swagger.io
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
**host** , **basePath** はそのまんまですね。
サンプルでは次のようになっています。
```
host: petstore.swagger.io
basePath: /v2
**schemes** は利用可能なスキームを列挙します。
サンプルでは次のようになっています。
```
schemes:
- http
これを増やすと、プレビューページで実行する際のスキームの選択肢が増えます。
---
**paths** は一番重要なところです。
次のような情報を持ちます。
※ 階層が深くなるので `.` で連結して階層を表現します。
| key | 型 | 用途 |
|:--- |:--- |:--- |
| /pets/{petId} | string | リソースへのパス |
| /pets/{petId}.get | object | |
| /pets/{petId}.get.tags | array<string> | Swagger の仕様書上でフィルタするためのタグ |
| /pets/{petId}.get.summary | string | エンドポイントに対する概要 |
| /pets/{petId}.get.description | string | エンドポイントに対する説明 |
| /pets/{petId}.get.operationId | string | operationId **FIXME** |
| /pets/{petId}.get.produces | array<string> | 出力可能なコンテンツタイプのリスト |
| /pets/{petId}.get.parameters | array<object> | パラメータの定義のリスト |
| /pets/{petId}.get.parameters[].in | string | パラメータの配置先 `path` か `query |
| /pets/{petId}.get.parameters[].name | string | パラメータ名 |
| /pets/{petId}.get.parameters[].description | string | パラメータの説明 |
| /pets/{petId}.get.parameters[].requied | boolean | 必須かどうか |
| /pets/{petId}.get.parameters[].type | string | 型 |
| /pets/{petId}.get.parameters[].format | string | 型のフォーマット |
| /pets/{petId}.get.response | object | レスポンスの定義 |
| /pets/{petId}.get.response."404" | object | 404 レスポンスの定義 |
| /pets/{petId}.get.response."404".description | string | 404 レスポンスの説明 |
| /pets/{petId}.get.response."200" | object | 200 レスポンスの定義 |
| /pets/{petId}.get.response."200".description | string | 200 レスポンスの説明 |
| /pets/{petId}.get.response."200".schema | object | 200 レスポンスのボディ |
| /pets/{petId}.get.security | array<object> | セキュリティの定義 |
| /pets/{petId}.get.security.api_key | array? | [] を指定すると未指定にできる **FIXME** |
| /pets/{petId}.get.security.petstore_auth | array<string> | 後述の `securityDefinitions` への参照となる `key` |
`/pets/{petId}` のように `{}` を使用することでプレースホルダにすることができます。
`get` の部分は `post` とすることで POST の定義を作成できます。
`tags` は、Swagger のプレビューページでフィルタリングするためのもののようです。
`summary`, `tags`, `produces` はオプショナルです。
`produces` はルート階層に配置することも可能です。
`parameters` に定義するオブジェクトは `in` キーに対して `path` を与えると、path で設置したプレースホルダに対応します。
`in` キーに対して `query` を与えると、 `?parameter.key=` のようなURLのクエリ部に対応します。
`parameters` の `type` は後述します。
`response` の子要素のキーは HTTP ステータスコードか `default` という文字列を指定する必要があります。
`5xx` とかできないのはちょっと不便に思いました。しかも、`default` って、、、。
`security` の子要素は、後述の `securityDefinitions` を参照することができるようです。
サンプルでは次のようになっています。
```
/pets/{petId}:
get:
tags:
- pet
summary: Find pet by ID
description: Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions
operationId: getPetById
produces:
- application/json
- application/xml
parameters:
- in: path
name: petId
description: ID of pet that needs to be fetched
required: true
type: integer
format: int64
responses:
"404":
description: Pet not found
"200":
description: successful operation
schema:
$ref: "#/definitions/Pet"
"400":
description: Invalid ID supplied
security:
- api_key: []
- petstore_auth:
- write_pets
- read_pets
---
**securityDefinitions** は次のような情報を持ちます。
| key | 型 | 用途 |
|:--- |:--- |:--- |
| api_key | object | api_key オブジェクトの定義 |
| api_key.type | string | **FIXME** |
| api_key.name | string | **FIXME** |
| api_key.in | string | **FIXME** |
| petstore_auth | object | **FIXME** |
| petstore_auth.type | string | 認証方式 |
| petstore_auth.authorizationUrl | string | 認証を行うページへのURL |
| petstore_auth.flow | string | **FIXME** |
| petstore_auth.scopes | array<string> | **FIXME** |
全体的に未確認なので、早急に確認してまとめたいと思います。
サンプルでは次のようになっています。
```
securityDefinitions:
api_key:
type: apiKey
name: api_key
in: header
petstore_auth:
type: oauth2
authorizationUrl: http://petstore.swagger.io/api/oauth/dialog
flow: implicit
scopes:
write_pets: modify pets in your account
read_pets: read your pets
---
**definitions** はいわゆる **モデル** の定義です。
次のような情報を持ちます。
| key | 型 | 用途 |
|:--- |:--- |:--- |
| Pet | object | Pet オブジェクトの定義 |
| Pet.type | string | Pet オブジェクトの型 |
| Pet.required | array<string> | Pet オブジェクトのプロパティの内必須なもののキーのリスト |
| Pet.properties | object | Pet オブジェクトのプロパティの定義 |
| Pet.properties.id | object | Pet オブジェクトのプロパティ `id` の定義 |
| Pet.properties.id.type | string | Pet オブジェクトのプロパティ `id` の型 |
| Pet.properties.id.format | string | Pet オブジェクトのプロパティ `id` のフォーマット |
| Pet.properties.name | string | Pet オブジェクトのプロパティ `name` の定義 |
| Pet.properties.name | string | Pet オブジェクトのプロパティ `name` の定義 |
| Pet.properties.name.type | string | Pet オブジェクトのプロパティ `name` の型 |
| Pet.properties.name.example | string | Pet オブジェクトのプロパティ `name` の例 |
ルート階層のキー名がモデル名になります。
具体的なところは `type` の説明を参照してください。
サンプルでは次のようになっています。
```
Pet:
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
$ref: "#/definitions/Category"
name:
type: string
example: doggie
photoUrls:
type: array
items:
type: string
tags:
type: array
items:
$ref: "#/definitions/Tag"
status:
type: string
description: pet status in the store
* **Type**
---
type には 次のものがあります。
* string
* float
* boolean
* array<?>
* integer
* object
`string` , `float` , `boolean` , `array` は特に解説することはありません。
上の解説の中には出てきませんでしたが `float` も利用できます。
[Yaml: types](http://yaml.org/type/) を参照してください。
※ rails で yaml を利用する分には気づきませんでしたが、 キーに対して子要素もない、設定値もないと言うのは許可されないようです。
`integer` には `format` がセットになります。
`format` をキーとして、 `int32` や `int64` を指定する事ができます。
`object` はいわゆるハッシュです。
いずれもキーを `$ref` とすることで、設定値を参照にすることができます。
この時の設定値は例えば `"#/definitions/Pet"` のようになります。
* **プロジェクト**
---
swagger を npm でインストールすると、`swagger` コマンドが利用できるようになります。
この `swagger` コマンドには `project` という、サブコマンドがあり、次の機能があります。
```
Commands:
create [options] [name] Create a folder containing a Swagger project
start [options] [directory] Start the project in this or the specified directory
verify [options] [directory] Verify that the project is correct (swagger, config, etc)
edit [options] [directory] open Swagger editor for this project or the specified project directory
open [directory] open browser as client to the project
test [options] [directory_or_file] Run project tests
generate-test [options] [directory] Generate the test template
肝となる `edit` は、どうもブラウザを起動して swagger-editor を開くもののようで、Vagrant で建てた VM では次のようなエラーが出ました。
```
$ swagger project edit
Starting Swagger Editor.
Opening browser to: http://127.0.0.1:43390/#/edit
{ [Error: Command failed: /bin/sh: xdg-open: command not found
] killed: false, code: 127, signal: null }
いずれ解決してやりたいと思います。
それはさておき、ひとまずプロジェクトを作ります。
次のコマンドで、プロジェクトを作成することができます。
```
swagger project create <プロジェクト名>
このコマンドを実行すると、次のような内容物を持つディレクトリが生成されます。
```
$ ls -l
total 28
drwxr-xr-x 6 vagrant vagrant 4096 Dec 2 05:11 api
-rw-r--r-- 1 vagrant vagrant 546 Dec 2 05:11 app.js
drwxr-xr-x 2 vagrant vagrant 4096 Dec 2 05:11 config
drwxrwxr-x 6 vagrant vagrant 4096 Dec 2 05:11 node_modules
-rw-r--r-- 1 vagrant vagrant 425 Dec 2 05:11 package.json
-rw-r--r-- 1 vagrant vagrant 31 Dec 2 05:11 README.md
drwxr-xr-x 3 vagrant vagrant 4096 Dec 2 05:11 test
作成した swagger の定義ファイルは、 `./api/swagger/` に配置します。
Git 管理に載せる場合は、まとまっていたほうがスッキリするのでひとまずこのプロジェクトにしたもので扱う予定です。
もっといい方法がわかったらまとめたいと思います。
また、他の機能はまだ試せていないので、そちらも試してまとめる予定です。
## まとめ
実際、swagger-editor は日本語に弱いらしく、変な入力になることが多々あります。
あまり使いやすいとは言いづらいですが、候補も出ますし、たまにテキスト全体をカット&ペーストすればエラーもちゃんと出してくれます。
プレビューのページはモデルあたりは非常に見難く、そのまま PDF化すれば納品物にできるぜというものではありません。
ドキュメント化という点では Swagger2Markup などの別のツールもあるみたいですが、まだ触れていないので色々試してまとめていきたいと思います。
それと、こういうものこそ Docker と連携しやすいっぽいですね。
まぁ、何にしろ yaml で記述できる手軽さは良いですね。
ちゃんとメンテされるドキュメンテーションへの第一歩ということで。
---
明日は @nullpopon@github さんです。
どんなことを記事にしてくれるのか楽しみです。
<!--
これを例に構築してみたい
curl -i "https://api.github.com/search/code?q=struct+in:file+language:cpp+repo:boostorg/hana"
-->