[swaggo]GoのGoDocを書いたら、Swaggerを出せるやばいやつ

# swaggo

今回紹介する[swaggo](https://github.com/swaggo/swag)は[yvasiyarov/swagger](https://github.com/yvasiyarov/swagger)にインスパイアを受けて、作成したOSSになります。現在、yvasiyarov/swaggerは開発が止まっているので、いくつかの問題が放置されたままになっています。（これはOSSなので仕方ないです）
swaggoは元の構文をそのまま流用して、機能追加をしているものになります。

- Swagger 2.0の対応
- 複雑な構造体の解析
- カスタムヘッダー
- example value
- セキュリティ

私がざっと見た限り、上の内容はswaggoで無ければ使えないっぽいです。

![swaggo.png](https://qiita-image-store.s3.amazonaws.com/0/110033/71423726-71f8-78a2-632b-b8e3e81f5e74.png)

## Swagger便利だけど、書くのがそもそもだるい

最近になって、よくSwaggerというワードを目にするようになりました。これはとても画期的で、APIに実際にリクエストを投げれるドキュメントが出来上がります。
しかし、このドキュメントのコードを書くのが結構面倒！

もし、既に実装されているコードからSwaggerを生み出すことが出来るものがあったら、最高ですよね？ということで、今回は[swaggo](https://github.com/swaggo)を使ったドキュメント生成を紹介します。

## 実装方法

だらだら書いても仕方ないので、さくっとコード例を書きます。
今回の記事に使ったコードは[こちら](https://github.com/swaggo/swag/tree/master/example/celler)にあります。

### General API Info

Swaggerの基本情報をmain.go（オプションで変えることも出来ます）に書きます。
[設定出来る項目](https://github.com/swaggo/swag#general-api-info)

```go
package main

import (
	"github.com/gin-gonic/gin"
	"github.com/swaggo/gin-swagger"
	"github.com/swaggo/gin-swagger/swaggerFiles"
	"github.com/swaggo/swag/example/celler/controller"
	_ "github.com/swaggo/swag/example/celler/docs"
)

// @title Swagger Example API
// @version 1.0
// @description This is a sample server celler server.
// @termsOfService http://swagger.io/terms/

// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:8080
// @BasePath /api/v1

// @securityDefinitions.basic BasicAuth

// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization

// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

// @securitydefinitions.oauth2.implicit OAuth2Implicit
// @authorizationurl https://example.com/oauth/authorize
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

// @securitydefinitions.oauth2.password OAuth2Password
// @tokenUrl https://example.com/oauth/token
// @scope.read Grants read access
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

// @securitydefinitions.oauth2.accessCode OAuth2AccessCode
// @tokenUrl https://example.com/oauth/token
// @authorizationurl https://example.com/oauth/authorize
// @scope.admin Grants read and write access to administrative information
func main() {
	r := gin.Default()

	c := controller.NewController()

	v1 := r.Group("/api/v1")
	{
		accounts := v1.Group("/accounts")
		{
			accounts.GET(":id", c.ShowAccount)
			accounts.GET("", c.ListAccounts)
			accounts.POST("", c.AddAccount)
			accounts.DELETE(":id", c.DeleteAccount)
			accounts.PATCH(":id", c.UpdateAccount)
			accounts.POST(":id/images", c.UploadAccountImage)
		}
		/...
	}
	r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
	r.Run(":8080")
}
```

### API Operation

エンドポイントに関する情報書きます。
[設定出来る項目](https://github.com/swaggo/swag#api-operation)
よく使う項目をピックアップして説明致します。

```go
package controller

//...

// ShowAccount godoc
// @Summary Show a account
// @Description get string by ID
// @Accept  json
// @Produce  json
// @Param  id path int true "Account ID"
// @Success 200 {object} model.Account
// @Failure 400 {object} controller.HTTPError
// @Failure 404 {object} controller.HTTPError
// @Failure 500 {object} controller.HTTPError
// @Router /accounts/{id} [get]
func (c *Controller) ShowAccount(ctx *gin.Context) {
	id := ctx.Param("id")
	aid, err := strconv.Atoi(id)
	if err != nil {
		NewError(ctx, http.StatusBadRequest, err)
		return
	}
	account, err := model.AccountOne(aid)
	if err != nil {
		NewError(ctx, http.StatusNotFound, err)
		return
	}
	ctx.JSON(http.StatusOK, account)
}
```

##### エンドポイントの説明

```go
// @Summary Show a account`
// @Description get string by ID`
```

#### MimeType

```go
// @Accept  json
// @Produce  json
```

APIが許可しているMimeTypeを設定できます。
[設定出来るMimeType](https://github.com/swaggo/swag#mime-types)

#### Param

```go
// @Param パラメーター名 種類 型 必須要素か？ コメント 
```

```go
// @Param id path int true "Account ID"
// @Param q query string false "name search by q"
// @Param account body model.AddAccount true "Add account"
// @Param Authorization header string true "Authentication header"
```

- path: パスパラメーター
- query: クエリパラメータ
- body: そのまんまです
- header: カスタムリクエストヘッダー

パラメーターの型として、string, int, fileなどが利用が出来ます。

#### Result

```go
// @Success ステータスコード パラメーターの型 データの型 コメント 
```

```go
// @Success 200 {object} model.Account
// @Failure 400 {object} controller.HTTPError
// @Failure 404 {object} controller.HTTPError
// @Failure 500 {object} controller.HTTPError
```

成功のパターンと失敗のパターンを必要分だけ用意します。

#### 構造体の指定の仕方

```go
// @Success 200 {object} model.Account
```

上記のように書くと、`package名.構造体名`のような感じで探しに行って、ヒモ付を行なってくれます。

```go
package model

// ...

type Account struct {
	ID   int    `json:"id" example:"1"`
	Name string `json:"name" example:"account name"`
}
```

`example`に設定された値は、Swaggerで出した時の具体例として設定されます。
`json`に設定された値は、JSONのキー名を決定します。
つまり、上記の例の場合、`{"id": 1, "name": "account name"}`というJSONが出来上がります。

#### Security

```go
// @Security ApiKeyAuth
```

General API Infoで定義したセキュリティを使うことが出来ます。これの使い方については、別の記事で詳しく取り上げる予定です。  
[https://swagger.io/docs/specification/2-0/authentication/:title]

#### Router

```go
// @Router /accounts/{id} [get]
```

エンドポイントのURIとメソッドを指定します。このURIはGeneral API infoでのBasePathからの相対パスを設定する必要があります。

```go
// @BasePath /api/v1
```

今回の場合だと、最終的に出来上がるエンドポイントは、/api/v1/accounts/{id}となります。

### Swaggerコード生成

```console
$ go get -u github.com/swaggo/swag/cmd/swag
$ swag init
```

上記をmain.goのあるパスで実行すると、docsフォルダが出来上がります。そこに、swaggerドキュメントに関するコードが生成されます。

```console
.
├── README.md
├── controller
├── docs <-- これが自動生成される
│   ├── docs.go
│   └── swagger
│       ├── swagger.json
│       └── swagger.yaml
├── main.go
└── model
```

```console
i -h
NAME:
   swag init - Create docs.go

USAGE:
   swag init [command options] [arguments...]

OPTIONS:
   --generalInfo value, -g value  Go file path in which 'swagger general API Info' is written (default: "main.go")
   --dir value, -d value          Directory you want to parse (default: "./")
   --swagger value, -s value      Output the swagger conf for json and yaml (default: "./docs/swagger")
```

ちなみに、このinitコマンドには、上記のようなオプションがあるので、自分の環境に合わせて変更が可能です。

```json
//...
"/accounts/{id}": {
            "get": {
                "description": "get string by ID",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "summary": "Show a account",
                "operationId": "get-string-by-int",
                "parameters": [
                    {
                        "type": "integer",
                        "description": "Account ID",
                        "name": "id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/model.Account"
                        }
                    },
                    "400": {
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/controller.HTTPError"
                        }
                    },
                    "404": {
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/controller.HTTPError"
                        }
                    },
                    "500": {
                        "schema": {
                            "type": "object",
                            "$ref": "#/definitions/controller.HTTPError"
                        }
                    }
                }
            },
//...
```

### 実際にSwaggerからリクエストを投げてみよう

swaggerのコードを読み込んで、使うのも良いですが、今回はswaggoが用意している便利な関数を使って、UIを呼び出したいと思います。ちなみに、そのコードは既に例に含まれています。

```go
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
```

[http://localhost:8080/swagger/index.html](http://localhost:8080/swagger/index.html)には既にSwaggerUIが展開されており、リクエストが出来る状態になっています。
このハンドラは、gin Echo net/httpをサポートしています。対応していないものを使っている場合は、自分でSwaggerUIを用意する必要があります。（PR投げれば種類を増やせます！）

![スクリーンショット 2018-03-12 13.34.33.png](https://qiita-image-store.s3.amazonaws.com/0/110033/5772b44f-cb93-a460-d7c0-5c1fef4ee514.png)

## まとめ

現状、簡単なAPIなら十分ドキュメント生成に活用出来ます。是非使ってみてください。
あと、自分が関わっているOSSということもあり、意見募集中です！ｗ