読者です 読者をやめる 読者になる 読者になる

Swagger 拡張のメモ

個人的なメモ。

Swagger 2.0 についての説明は本家や適当に調べて下さい。
REST APIを記述せよ!Swagger紹介 #apijp - Groovyラボ

Swagger の紹介されているサイトでは、WebAPI と Swagger UI を同居させるパターンが多くて、public なサービスだと良いのかもですが、開発中だけ Swagger UI 欲しい場合はちょい不便。
というわけで、ビルド時に Swagger JSON を吐く時のメモです。

Swagger は v1 の時は Scala で書かれていて読むのしんどかった*1けど、v2 からは、Java で書き直されていて大分楽でした。

v2 といってるけど、jar 的には 1.5.2-M1 です。

今回は JAX-RS で使うのでそっちメインで追ってます。
.NET での利用(ASP.NET WebAPI や Azure API Apps 等?)方面はまったくノータッチ。
Swagger を使った ASP.NET Web API のドキュメント生成 - miso_soup3 Blog
Build web and mobile apps faster | ブチザッキ

パッケージは省略しています。~ は com.wordnik.swagger に置き換えて下さい。

Swagger generator

swagger-models.jar
~.models.Swagger

Swagger の構造持ってるやつ。add~ で色々追加していける。

Swagger ファイル(json/yaml) の出力

swagger-core.jar
~.util.Json/Yaml

Swagger インスタンスを食わすと出力

Json.mapper().writeValue(new File("api-docs.json"), swagger);
JAX-RS のリソースクラスを parse

swagger-jaxrs.jar
~.jaxrs.Reader

リソースクラスを食わすと、前述の Swagger インスタンスが返ってくる。
紐付く戻り値等のModel定義も全部返ってくるので、リソースクラスを食わすだけでOK

JAX-RS のリソースクラス/メソッド/戻り値等に情報を付与

swagger-annotations.jar
~.annotations パッケージ全般

アノテーション は RententionPolicy.RUNTIME になってるけど、war に Swagger の jar を含めなくても大丈夫そう。
RUNTIME になってるアノテーションでも classpath にいなくても平気らしい。*2

Java8 Date and Time API 対応

swagger-core.jar
~.jackson.TypeNameResolver

protected final static な Map インスタンスをいじる必要があるので拡張し難い
結局ここではやらなかった。

Java8 Optional 対応

swagger-core.jar
~.jackson.ModelResolver

private なメソッド boolean _isOptionalType でやってるので拡張し難そう。
Optional は使ってないしスルー。

無視するクラス指定

swagger-core.jar
~.converter.ModelConverters#add Class/Package ToSkip

List/Array のモデルを指定する

swagger-models.jar
~.models.ArrayModel

~.annotations.ApiOperation なら responseContainer も指定出来て List 等のコレクション表現が出来るが、 ~.annotations.ApiResponse では response のクラスしか指定出来ない。
なので、ApiResponse での定義を後で差し替えるために使った。

swagger.addDefinition("_ErrorCollection",
    new ArrayModel().items(new RefProperty().asDefault("ErrorDto")));
メソッドのパラメーター関係

swagger-models.jar
~.models.parameters パッケージ

よく使うのは SerializableParameter を実装した Cookie/Form/Header/Path/Query
type/format は SerializableParameter で OK だが、defaultValue は各具象クラスに定義。

JAX-RS メソッドパラメーターの解析

swagger-jaxrs.jar
~.jaxrs.ext.SwaggerExtension
~.jaxrs.DefaultParameterExtension
~.jaxrs.ext.SwaggerExtensions

既存の DefaultParameterExtension では、@BeanParam に対応していない。
@DefaultValue は TODO not supported yet
今の作りだと大変そうな気もする。

自前の SwaggerExtension を実装し、DefaultParameterExtension と入れ替えると色々捗る。

SwaggerExtensions.getExtensions().clear();
SwaggerExtensions.getExtensions().add(new SwaggerBeanParamExtensions());

大元の呼び出し側は、先頭の Extension しか呼び出さない。
多分設計思想は各 Extension 内で 次の Extension があれば続けて呼ぶような感じのはずだが、
DefaultParameterExtension の実装が chain してくれないので、自前の Extension を先に処理させる必要がある。

Swagger UI でのメソッドパラメーターの表示

swagger-client.js

Operation のとこ
getModelSignature の値が表示されてそう。
この値を決めるのが、SerializableParameter の type/format の値。

その他諸々

個人的に使ってるのは、ドキュメント用の自前アノテーション追加 & SwaggerExtension 差し替え から Reader で JAX-RS リソースクラス群を読まして出来た Swagger にちょいちょい細工してから JSON で吐くのを Gradle でやってます。

Reader で読んだ Swagger を加工は良く使いそうな気がします。
例:Servlet Filter で処理するような Cookie や Headder の値があるが、当然リソースメソッドにはパラメーターとして定義していないケース。
Swagger UI 上にパラメーターとして表示しないとAPIの呼び出しが出来ないので Swagger UI の魅力半減!
って時に、Reader で読んだ Swagger に対してこんな感じで追加してあげる。
コード例は groovy

swagger.paths.each { k, v ->
  v.operations.each {
    it.addParameter(additionalHeaderParam)
  }
}

自前で拡張していくと本家のバージョン上げた時大変そうですが、使い捨てるならバージョン上げることも無さげなのでそんな気にする必要も無いかと。

サンプルはこちら sample/jaxrs_swagger_output_json at master · OdaShinsuke/sample · GitHub

Swagger 自体の欠点は情報を付与しようとしたら、アノテーション書く量が増えること。
しょうがないっちゃーしょうがないけど、JavaDoc から何とかならないかな?
あとは、Arquillian で書いたテストとテスト結果から情報補完してくれたら楽しそう!

*1:implicit 使って呼び出してるメソッドとか Java でどうやったら呼べるのかとかさっぱりやった

*2:体験談で公式情報ではないです。公式な情報持ってたら教えて下さい。