openapi-generator-cli を使って生成したコードを使ってリクエストとレスポンスに使う

# openapi-generator-cli とは？

[openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator-cli)は、OpenAPIで書かれた仕様書からコードを生成するものです。

特にGo言語用というわけではなく、WebApplicationFrameworkに従ったコードも生成してくれます。どんな言語、Frameworkに対応しているかは、[READMEのOverview](https://github.com/OpenAPITools/openapi-generator?tab=readme-ov-file#overview)を見てみると良いでしょう。Perlもありますね(ないと思ってた)。

今回、WebApplicationFrameworkに[Fiber](https://gofiber.io/)を使ったのですが、残念ながら、Fiberには対応していません。ですが、生成したコードを使うことは出来ます。

## openapi-cli-generatorを動かす

今回は、Dockerで動かしました。openapi-generator-cliのオプションとしては、`generate`から後ろですが、オプションが多いですね。

```sh
docker run --rm -u "${UID}:${GID}" -v "${PWD}:/local"  \
	    openapitools/openapi-generator-cli generate -g go \
		--skip-validate-spec \
		--global-property models,supportingFiles,modelDocs \
		--additional-properties withGoMod=false \
		--package-name package名 \
		-i /local/docs/openapi/openapi.json \
		-o /local/internal/interfaces/api/generated/package名/
```

一つずつ見ていきましょう。

- `generate` ... コードを生成する。そのままです。以下は generateのオプションです
- `-g go` ... Go用のコードを生成する
- `--skip-validate-spec` ... 仕様書の書式のチェックをスキップ
- `--global-property models,supportingFiles,modelDocs` ... models,supportingFiles,modelDocsを生成する
- `--additional-properties withGoMod=false` ... go.mod を作らない
- `--package-name package名` ... package名を指定する
- `-i /local/docs/openapi/openapi.json` ... 仕様書を指定する
- `-o /local/internal/interfaces/api/generated/package名/` ... 出力先の指定(/localに、current directoryをmountしています)

`--global-property` を指定しないと、生成したコードをそのまま使えませんでした。今回使用する用途的に必要そうな最低限の組み合わせになります(modelDocsは別に不要ですが)。

## 生成されたコードを使う

openapi-generator-cliが生成するのは、主に、

- `client.go` ... APIを叩くためのコード
- `configuration.go` ... エンドポイントのURL等の設定っぽい
- `model_*パス_request.go` ... 各APIのリクエストに関する型や実装
- `model_*パス_response.go` ... 各APIのレスポンスに関する型や実装

### GET

で使え...ません。GETのAPIに関しては、`model_*パス*request.go`が存在しないので、fiberの`QueryParser`に渡せません。

`--global-property` に `apis` を渡せば、`api_*.go`にGETようの型は作ってくれるのですが、構造体のメンバがprivateなので、Fiberの`QueryParser`に渡すことができません。残念。

コードを参考にして、構造体を作れば楽なのは楽かもしれません。

### POST

`model_*パス*request.go`の中にJSON用の構造体が入っていますので、それが使えます。例えば、こんな感じですね。

```go
type YourApiRequest struct {
	UserID string `json:"user_id"`
}
```

`fiber`の`BodyParser`にそのまま渡すことが出来ます。

```go
  c.BodyParser(req)
```
#### レスポンス

`model_*パス*response.go`の中にJSON用の構造体が入っていますので、それが使えます。例えば、こんな感じですね。

```go
type YourApiNameGet200Response struct {
	Result []YourApiNameGet200ResponseResultInner `json:"result"`
}
```

この型通りにデータを作ってやって、JSONにすれば、そのままレスポンスに使えます。

```go
  data := YourApiNameGet200ResponseResultInner{
   // 略
  }
  c.JSON(your_pacakge.NewYourApiResponse(data))
```

## 各POSTの型にダミーのメソッドを作る

`codegen/openapi-request-generator.sh` というプログラムで、`CanWrap`というメソッドを各作ります。やってることは、以下のように超単純です。

```sh
#!/bin/bash

dirs=$1

if [ "$dirs" = "" ]; then
  dirs="your_pacakge_name"
fi

for dir in $dirs; do
  echo -e "package $dir\n" > internal/interfaces/api/generated/$dir/generated_func.go;
  grep 'Request struct' -r internal/interfaces/api/generated/$dir | grep type |cut -d ' ' -f 2 | sort | xargs -I {} echo "func (*{}) CanWrap() { }" >> internal/interfaces/api/generated/$dir/generated_func.go
  go fmt internal/interfaces/api/generated/$dir/generated_func.go
done
```
各Postリクエストの型に対して、`CanWrap`という何もしないメソッドを定義しています。`CanWrap()`は、`RequestWrapper` interfaceを満たすためのメソッドです。

```go
type RequestWrapper interface {
        CanWrap()
}
```

これで何が嬉しいかと言うと...[前回書いたやつ](https://qiita.com/ktat/items/9a4c89a1cc859b74b30f)が、できるようになりますね。

以下、略。

## 終わり

以上、openapi-generator-cli を使えば、楽できますよという話でした。