go-swaggerの紹介

## Swaggerとは

OpenAPI Initialiveが提唱しているRestful APIの仕様記述に関する標準化を目指して作られたフォーマット、およびそのツール群です。yamlやjsonでAPI仕様を記述し、そのワークフローを支える幾つかのツールが提供されています。

|ツール名 | 概要 |
|:--|:--|
| Swagger Core | Swaggerの仕様書をJava向けに読み書きするライブラリ |
| Swagger Codegen | Swaggerの仕様書からクライアント／サーバのコードベースを生成するツール群、サードパーティー含め様々な言語向けに提供されています |
| Swagger UI | Swaggerで記述されたAPI仕様をブラウザ側から確認するためのUI |
| [Swagger Editor](http://editor.swagger.io/#/) | ブラウザベースで作成されたSwaggerの仕様を記述するためのエディタ |

詳しい仕様に関しては、公式サイトを参照ください。
http://swagger.io/

### sample
Swaggerで記載されたAPIのサンプルを見てみましょう。

Sample: [Petstore](https://raw.githubusercontent.com/swagger-api/swagger-spec/master/examples/v2.0/json/petstore-expanded.json)

## GolangとSwagger
公式のSwagger CodegenではGolangの対応はありませんが、VMWare製OSSの`go-swagger/go-swagger` を利用することでGolangでもSwaggerの恩恵をうけることが可能です。

### go-swaggerを使ってみる

まずはgo-swaggerをInstallしてください。今のところ、安定版ではなく、安定版ができ次第`gopkg.in`で利用可能にするとのことでした。

```shell-session
$ go get -u github.com/go-swagger/go-swagger/cmd/swagger
```

上記をinstallすると、`swagger`コマンドが使えるようになりますので、先ほどのサンプルを利用してAPIサーバを吐き出してみましょう。

```shell-session
$ cd $GOPATH/src/path/to/your_pj

#サーバサイドのコード生成
$ swagger generate server -f https://raw.githubusercontent.com/swagger-api/swagger-spec/master/examples/v2.0/json/petstore-expanded.json -a petstore
2015/12/13 18:11:42 rendered model template: Pet
2015/12/13 18:11:42 generated model Pet
2015/12/13 18:11:42 rendered model template: NewPet
2015/12/13 18:11:42 generated model NewPet
2015/12/13 18:11:42 rendered model template: Error
2015/12/13 18:11:42 generated model Error
2015/12/13 18:11:42 rendered handler template: petstore.FindPets
2015/12/13 18:11:42 generated handler petstore.FindPets
2015/12/13 18:11:42 rendered parameters template: petstore.FindPetsParameters
2015/12/13 18:11:49 generated parameters petstore.FindPetsParameters
2015/12/13 18:11:49 rendered responses template: petstore.FindPetsResponses
2015/12/13 18:11:49 generated responses petstore.FindPetsResponses
2015/12/13 18:11:49 rendered handler template: petstore.FindPetByID
2015/12/13 18:11:49 generated handler petstore.FindPetByID
2015/12/13 18:11:49 rendered parameters template: petstore.FindPetByIDParameters
2015/12/13 18:11:49 generated parameters petstore.FindPetByIDParameters
2015/12/13 18:11:49 rendered responses template: petstore.FindPetByIDResponses
2015/12/13 18:11:49 generated responses petstore.FindPetByIDResponses
2015/12/13 18:11:49 rendered handler template: petstore.AddPet
2015/12/13 18:11:49 generated handler petstore.AddPet
2015/12/13 18:11:49 rendered parameters template: petstore.AddPetParameters
2015/12/13 18:11:49 generated parameters petstore.AddPetParameters
2015/12/13 18:11:49 rendered responses template: petstore.AddPetResponses
2015/12/13 18:11:49 generated responses petstore.AddPetResponses
2015/12/13 18:11:49 rendered handler template: petstore.DeletePet
2015/12/13 18:11:49 generated handler petstore.DeletePet
2015/12/13 18:11:49 rendered parameters template: petstore.DeletePetParameters
2015/12/13 18:11:49 generated parameters petstore.DeletePetParameters
2015/12/13 18:11:49 rendered responses template: petstore.DeletePetResponses
2015/12/13 18:11:49 generated responses petstore.DeletePetResponses
2015/12/13 18:11:49 rendered builder template: petstore.SwaggerPetstore
2015/12/13 18:11:49 rendered embedded Swagger JSON template: server.SwaggerPetstore
2015/12/13 18:11:49 rendered configure api template: petstore.ConfigureSwaggerPetstore
2015/12/13 18:11:49 rendered doc template: petstore.SwaggerPetstore
2015/12/13 18:11:49 rendered main template: server.SwaggerPetstore

# client側のコード生成
$ swagger generate client -f https://raw.githubusercontent.com/swagger-api/swagger-spec/master/examples/v2.0/json/petstore-expanded.json -a petstore
2015/12/13 18:12:17 rendered model template: Pet
2015/12/13 18:12:17 generated model Pet
2015/12/13 18:12:17 rendered model template: NewPet
2015/12/13 18:12:17 generated model NewPet
2015/12/13 18:12:17 rendered model template: Error
2015/12/13 18:12:17 generated model Error
2015/12/13 18:12:17 rendered client parameters template: petstore.AddPetParameters
2015/12/13 18:12:20 rendered client responses template: petstore.AddPetResponses
2015/12/13 18:12:20 rendered client parameters template: petstore.DeletePetParameters
2015/12/13 18:12:20 rendered client responses template: petstore.DeletePetResponses
2015/12/13 18:12:20 rendered client parameters template: petstore.FindPetByIDParameters
2015/12/13 18:12:20 rendered client responses template: petstore.FindPetByIDResponses
2015/12/13 18:12:20 rendered client parameters template: petstore.FindPetsParameters
2015/12/13 18:12:20 rendered client responses template: petstore.FindPetsResponses
2015/12/13 18:12:20 rendered operation group client template: petstore.PetstoreClient
2015/12/13 18:12:20 rendered client embedded swagger JSON template: client.SwaggerPetstoreClient
2015/12/13 18:12:20 rendered client facade template: client.SwaggerPetstoreClient
```

上記でサーバ・クライアント双方のコードが生成されました。内容は下記です。

```
.
├── client
│   ├── petstore
│   │   ├── add_pet_parameters.go
│   │   ├── add_pet_responses.go
│   │   ├── delete_pet_parameters.go
│   │   ├── delete_pet_responses.go
│   │   ├── find_pet_by_id_parameters.go
│   │   ├── find_pet_by_id_responses.go
│   │   ├── find_pets_parameters.go
│   │   ├── find_pets_responses.go
│   │   └── petstore_client.go
│   ├── swagger_petstore_client.go
│   └── swagger_petstore_embedded_spec.go
├── cmd
│   └── swagger-petstore-server
│       ├── configure_swagger_petstore.go
│       ├── doc.go
│       ├── embedded_spec.go
│       └── main.go
├── models
│   ├── error.go
│   ├── new_pet.go
│   └── pet.go
└── restapi
    └── petstore
        ├── add_pet.go
        ├── add_pet_parameters.go
        ├── add_pet_responses.go
        ├── delete_pet.go
        ├── delete_pet_parameters.go
        ├── delete_pet_responses.go
        ├── find_pet_by_id.go
        ├── find_pet_by_id_parameters.go
        ├── find_pet_by_id_responses.go
        ├── find_pets.go
        ├── find_pets_parameters.go
        ├── find_pets_responses.go
        └── swagger_petstore_api.go
```

仕様をきっちりと記載さえしていればgodoc等も良しなに生成されているようでした。
この時点でcmdまで生成されているので、

```shell-session
$ go install <your swagger pj>/cmd/swagger-petstore-server
$ swagger-petstore-server
serving swagger petstore at http://127.0.0.1:62665
```

とするだけでサンプルのサーバが立ち上がる状態になっています。
また、その実装については、`cmd/swagger-petstore-server/configure_swagger_petstore.go`を見ていただければ、実装すべき内容すぐわかるようになっています。

```go
package main

import (
	"github.com/go-swagger/go-swagger/errors"
	"github.com/go-swagger/go-swagger/httpkit"
	"github.com/go-swagger/go-swagger/httpkit/middleware"

	"github.com/y-matsuwitter/swagger-sample/restapi/petstore"
)

// This file is safe to edit. Once it exists it will not be overwritten

func configureAPI(api *petstore.SwaggerPetstoreAPI) {
	// configure the api here
	api.ServeError = errors.ServeError

	api.JSONConsumer = httpkit.JSONConsumer()

	api.JSONProducer = httpkit.JSONProducer()

	api.AddPetHandler = petstore.AddPetHandlerFunc(func(params petstore.AddPetParams) middleware.Responder {
		return middleware.NotImplemented("operation addPet has not yet been implemented")
	})
	api.DeletePetHandler = petstore.DeletePetHandlerFunc(func(params petstore.DeletePetParams) middleware.Responder {
		return middleware.NotImplemented("operation deletePet has not yet been implemented")
	})
	api.FindPetByIDHandler = petstore.FindPetByIDHandlerFunc(func(params petstore.FindPetByIDParams) middleware.Responder {
		return middleware.NotImplemented("operation find pet by id has not yet been implemented")
	})
	api.FindPetsHandler = petstore.FindPetsHandlerFunc(func(params petstore.FindPetsParams) middleware.Responder {
		return middleware.NotImplemented("operation findPets has not yet been implemented")
	})

}
```

## Clientを触ってみる

clientコードまで生成されているので、これを利用してAPIを叩いてみましょう。

```go
package main

import (
	"log"

	apiclient "github.com/y-matsuwitter/swagger-sample/client"
	"github.com/y-matsuwitter/swagger-sample/client/petstore"
)

func main() {
	api := apiclient.Default.Petstore
	res, err := api.FindPetByID(petstore.FindPetByIDParams{1})
	log.Printf("%+v\n", res)
	log.Println(err)
}
```

先ほどのsampleのjsonでの仕様書だと`petstore.swagger.io`をhostとして参照する仕様となっていたので、当然上記のサンプルはそちらのhostを叩きに行きます。
ちなみにこの投稿を書いている時点では上記ホストとsampleのjson仕様書の間に乖離があるようで、正常に動作しませんでした。（というかインターネット上に幾つか存在するpetstoreの仕様書が古い？）

## 最後に
Goでのマイクロサービス的な開発においてはこういったサーバ／クライアント間を統一的に記述できる仕組みからコードを生成してくれる仕組みはありがたいなと感じましたが、まだまだ使用してみた感じでは未完成な部分（日本語での記述で不具合起きるなど）が多く、今のところ自分にとってのswaggerはあくまで仕様書の記述にとどまるのではないかなと思いました。
あと、似たような領域ではjson-hyper-schemaがありますが、そちらとの比較もしてみたいなと思いました。