今回はSwagger Codegenを紹介します。
Swagger Codegenを使うと、Swagger Spec仕様を記述したYAMLやJSONファイルからAPIコンシューマのドライバコードやAPIプロバイダのスタブコードを自動生成できます。Swagger UIと合わせてマスターすることで、Swagger Specを中核に置き、ドキュメントもコードも自動生成することが可能になります。
出力できる言語もC#、Java、golang、負荷試験のためのJMeterなど多岐に渡っており、好みや用途に応じて選択可能な点も見逃せません。インストールから動作確認までの手順をぜひ試してみてください。
インストールと設定
ターミナルにて以下のとおりbrewコマンドを投入し、Swagger Codegenをインストールします。インストール作業はこれで終わりで、特段の設定は必要ありません。
$ brew install swagger-codegen
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).
==> Updated Formulae
apt-dater
==> Downloading https://homebrew.bintray.com/bottles/swagger-codegen-2.2.1.sierr
Already downloaded: /Users/yujishono/Library/Caches/Homebrew/swagger-codegen-2.2.1.sierra.bottle.tar.gz
==> Pouring swagger-codegen-2.2.1.sierra.bottle.tar.gz
/usr/local/Cellar/swagger-codegen/2.2.1: 5 files, 14.3M
機能紹介と基本的な使い方
まずは、APIコンシューマのドライバコードを自動生成してみましょう。
swagger-codegenコマンドを実行します。[-i]オプションでインプットとなるSwagger Spec仕様のJSONを指定し、[-l]オプションで出力するコードの言語を指定します。
$ swagger-codegen generate -i http://petstore.swagger.io/v2/swagger.json -l java
[main] INFO io.swagger.parser.Swagger20Parser - reading from http://petstore.swagger.io/v2/swagger.json
[main] INFO io.swagger.codegen.ignore.CodegenIgnoreProcessor - No .swagger-codegen-ignore file found.
<中略>
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /Users/yujishono/swagger/codegenSample/./src/main/java/io/swagger/client/ProgressResponseBody.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /Users/yujishono/swagger/codegenSample/./.swagger-codegen-ignore
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /Users/yujishono/swagger/codegenSample/./LICENSE
生成されたコードを確認します。Mavenプロジェクト形式でAPIのソースコードやテストコードが出力されており、テストやビルドも併せて実行可能です。
今度はAPIプロバイダ側のスタブコードを作成してみましょう。-lオプションにてnodejs-serverを指定します。
$ swagger-codegen generate -i http://petstore.swagger.io/v2/swagger.json -l nodejs-server
[main] INFO io.swagger.parser.Swagger20Parser - reading from http://petstore.swagger.io/v2/swagger.json
[main] INFO io.swagger.codegen.ignore.CodegenIgnoreProcessor - No .swagger-codegen-ignore file found.
<後略>
npmコマンドを実行すると、8080ポートにてスタブが起動されます。
$ npm start
> swagger-petstore@1.0.0 prestart /Users/yujishono/swagger/codegenSample
> npm install
> swagger-petstore@1.0.0 start /Users/yujishono/swagger/codegenSample
> node index.js
Your server is listening on port 8080 (http://localhost:8080)
Swagger-ui is available on http://localhost:8080/docs
起動されたAPIプロバイダにブラウザからアクセスしてみましょう。「http://localhost:8080/v2/pet/1」にアクセスすると、JSON形式で値が返却されます。
{
"photoUrls": [
"aeiou"
],
"name": "doggie",
"id": 123456789,
"category": {
"name": "aeiou",
"id": 123456789
},
"tags": [
{
"name": "aeiou",
"id": 123456789
}
],
"status": "aeiou"
}
ちなみに、コードだけでなく、Swagger UIのAPI仕様も併せて生成されています。「http://localhost:8080/docs」にアクセスすると、生成されたAPI仕様を確認できます。
APIコンシューマからAPIプロバイダの呼び出し
Swagger Codegenにて生成したAPIコンシューマのドライバコードから、同じく生成・起動したAPIプロバイダのスタブにアクセスしてみましょう。
APIコンシューマの宛先を先ほど起動したAPIプロバイダに変更します。
[ApiCient.java]の[basePath]変数の値をサーバのアドレス「http://localhost:/8080/v2」に変更します。
APIプロバイダの仕様に合わせて、APIコンシューマのテストコードを編集します。
[ApiTest.java]の[getPetByIdTest]メソッドを以下の通り修正します。
// 前略
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
/**
* API tests for PetApi
*/
//@Ignore
public class PetApiTest {
// 中略
@Test
public void getPetByIdTest() throws ApiException {
Long petId = Long.parseLong("1");
Long exp = Long.parseLong("123456789");
Pet response = api.getPetById(petId);
assertThat(response.getId(), is(exp));
assertThat(response.getName(), is("taro"));
}
//後略
[PetApiTest.java]を右クリックし、[ファイルをテスト]をクリックすると、以下の通り、テスト結果が表示されます。
[getPetByIdTest]のテストにおいて、[taro]と返却されると思っていたのが、[doggie]と返却されていることがわかります。
クライアント側とサーバ側をつなぐインタフェースの認識齟齬は発生しやすく、テストコードをきちんと書いて、網羅的に仕様の確認を行う重要性が増しています。
生成するコードのカスタマイズ
生成されたコードがそのまま使える場合は問題ありませんが、一部修正が必要な場合はどうすれば良いでしょうか。
前述のような、生成されたコードを直接修正するやり方は、修正は容易ですが再度コードを自動生成すると修正部分が元に戻ってしまいます。また、そもそもSwagger Specおよび生成したドキュメントと内容の乖離が出てしまいます。
Swagger Codegenはjmustacheテンプレートエンジンを使ってコード生成します。例えば、PetApi.javaは以下のテンプレートを元に生成されています。{{}}で括られた箇所が変数です。生成するコードをカスタマイズするには、テンプレートを修正すればよく、Swagger Specとコードとの整合性を保持することが可能になります。
$ git clone https://github.com/swagger-api/swagger-codegen
$ cat swagger-codegen/modules/swagger-codegen/src/main/resources/Java/api.mustache
{{>licenseInfo}}
package {{package}};
import com.sun.jersey.api.client.GenericType;
import {{invokerPackage}}.ApiException;
import {{invokerPackage}}.ApiClient;
import {{invokerPackage}}.Configuration;
import {{modelPackage}}.*;
import {{invokerPackage}}.Pair;
{{#imports}}import {{import}};
{{/imports}}
{{^fullJavaUtil}}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
{{/fullJavaUtil}}
{{>generatedAnnotation}}
{{#operations}}
public class {{classname}} {
private Map defaultHeaderMap = new HashMap();
private String basePath = "{{basePath}}";
private ApiClient {{localVariablePrefix}}apiClient;
Generation Gapパターンによる自動生成部の分離
Swaggerの分野に限らず、ドキュメントとコードの整合性の保持はシステム開発における、大きな課題の一つです。MDA(Model Driven Architecture)や4GL(4th generation language)といった先進的な取り組みは長期的にみて必ずしも成功したとは言えず、アカデミックな領域においても、Code Generation And Optimizationシンポジウムなどにおいて、コード自動生成について古くから様々なプラクティスが検討されてきました。
コード自動生成のプラクティスとしては、自動生成部と手動書き換え部の分離を実現する「Generation Gapパターン」が有名です。自動生成部は抽象クラスとし、生成後変更しません。手動で書き換えたい箇所については、抽象クラスを継承したクラスにて実現します。これにより、Swagger Specと実装コードの整合性を保持します。
* * *
今回は、Swagger Codegenを用いて、Swagger Spec仕様のJSONファイルからクライアントのドライバコードやサーバのスタブコードを自動生成しました。
Swaggerでは、Swagger Specを中心に置き、ドキュメントとコードをシームレスにつなぐことができます。Swagger SpecベースのJSONファイルを作成することで、Swagger UIのAPIドキュメントを生成できるだけでなく、Swagger Codegenによりコードも生成できることがご理解いただけたのではないでしょうか。
マイクロサービスが進展する状況においては、Design By Contractに代表されるように、呼び出し側のAPIクライアントと、呼び出される側のAPIサーバ間の仕様を明確にするアプローチが重要視されます。Swaggerもそれを継承したプロダクトの一つです。
さらには、クラウドを前提としたいわば「クラウドネイティブ」を志向した開発スタイルが進展してきており、「サーバレス」「レスポンシブ」「コレオグラフィー」などといった新しいアーキテクチャスタイルへ変貌しつつあります。今回はGeneration Gapパターンを紹介しましたが、Swaggerを独立した単一のツールと捉えず、これらの状況を鑑みた適用方法を検討すると良いでしょう。
次回は、コードからSwagger SpecベースのJSONファイルを作成するSwagger Coreを紹介します。
著者紹介
正野 勇嗣 (SHONO Yuji ) - NTTデータ シニア・エキスパート
2011年頃まで開発自動化技術のR&Dに従事。その後、開発プロジェクト支援やトラブルシューティング等に主戦場を移す。「ソースコード自動生成」に加えて、JenkinsやMaven等の「ビルド自動化」、JsTestDriverやSelenium等の「テスト自動化」を扱うようになり、多様化する開発自動化技術動向に興味。
最近は第四の自動化であるInfrastructure as Code等の「基盤自動化」の魅力に惹かれている。開発自動化技術に関する雑誌・記事執筆も行う。2児のパパ。