Swagger自動生成スタブの継続的デプロイ
SwaggerにはYAMLからAPIクライアントやAPIサーバのコードを自動生成する機能があります。コードの自動生成を利用することでドメインの実装に集中でき、また、スタブを利用することでチーム開発を円滑に進められるといったメリットがあります。
しかし、コードの自動生成を利用すると、API仕様を更新したのにスタブは古いままといったライフサイクルのズレが起こりがちです。そこで、CIで継続的に自動生成を実行することで、常に最新のスタブを共有しながらチーム開発を行えるようになります。
本稿では、CIとGradleを利用してスタブコードを自動生成する方法を説明します。具体的には、CIで以下を実行します。
- 開発者がSwagger YAMLを変更する。
- Gradleでスタブコードを自動生成する。
- GradleでスタブコードからJARをビルドする。
- JARをデプロイする。
前提
以下のマルチプロジェクト構成を前提に説明します。
- ルートプロジェクト
Swaggerによるスタブコードの自動生成
ビルドスクリプトからswagger-codegen-cliを実行します。
// codegen/build.gradle configurations { swagger } dependencies { swagger 'io.swagger:swagger-codegen-cli:2.2.1' } task generateSwaggerServer(type: JavaExec) { description 'Generate source code for API server' main = 'io.swagger.codegen.SwaggerCodegen' classpath = configurations.swagger args 'generate', '-i', file("${project(':api').projectDir}/src/main/swagger/api.yaml"), '-o', file("$buildDir/swagger-server"), '-l', 'spring', '--additional-properties', 'dateLibrary=java8' }
swagger-codegen-cliに引数を渡すことで、自動生成されるコードをカスタマイズできます。上記の例では、Springのコードを生成する、Java 8の日付クラスを使うといったことを指定しています。
テンプレートのカスタマイズ
自動生成のテンプレートをカスタマイズするには、swagger-codegenリポジトリにあるテンプレートフォルダを使います。例えば、Springのテンプレートをカスタマイズするには以下のフォルダをローカルに配置します。
ビルドスクリプトではテンプレートの場所をswagger-codegen-cliの引数に渡します。
task generateSwaggerServer(type: JavaExec) { description 'Generate source code for API server' main = 'io.swagger.codegen.SwaggerCodegen' classpath = configurations.swagger args 'generate', '-i', file("${project(':api').projectDir}/src/main/swagger/api.yaml"), '-o', file("$buildDir/swagger-server"), '-t', file('template'), // テンプレートの場所 '-l', 'spring', '--additional-properties', 'dateLibrary=java8' }
スタブのビルド
自動生成されたコードをビルドします。ここではSpring BootでJARを生成します。
// stub/build.gradle buildscript { ext { springBootVersion = '1.4.0.RELEASE' } repositories { jcenter() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } plugins { id 'java' id 'spring-boot' } sourceCompatibility = 1.8 targetCompatibility = 1.8 sourceSets.main.java.srcDir file("${project(':codegen').buildDir}/swagger-server/src/main/java") sourceSets.main.resources.srcDir file("${project(':codegen').buildDir}/swagger-server/src/main/resources") repositories { jcenter() } dependencies { compile 'org.springframework.boot:spring-boot-starter-web' compile 'io.springfox:springfox-swagger2:2.5.0' compile 'io.springfox:springfox-swagger-ui:2.5.0' } compileJava.dependsOn project(':codegen').generateSwaggerServer
buildタスクを実行すると、JARファイルが出力されます。
./gradlew :stub:build
スタブのデプロイ
/stub/build/libs/stub.jar
に出力されたJARをサーバにデプロイします。Dockerを利用すると簡単に実行できるので便利です。
# stub/Dockerfile FROM java:8 EXPOSE 8080 COPY build/libs/stub.jar / ENTRYPOINT ["java", "-jar", "/stub.jar"]
# stub/docker-compose.yml version: "2" services: app: build: . ports: - "8080:8080"
以下のコマンドを実行すると、スタブが実行されます。古いスタブが実行されている場合は新しいものに置き換わります。
docker-compose build
docker-compose up -d
サーバの /v1
にアクセスしたらSwagger UIが表示されます。また、YAMLに定義済みのパスにアクセスしたら空のJSONが返ってきます。
今後の課題
swagger-codegen-cliが自動生成するコードは空の値を返す実装になっています。これではスタブの利用側に不親切なので、 example
セクションに書いたデータを返す実装にしてほしいと思います。
Gradleからswagger-codegenを利用するプラグインはすでにありますが、swagger-codegen-cliの最新版に追いついていないので、今回はJARを直接実行することにしました。本当はプラグインを利用する方がビルドスクリプトの可読性が高くてよいと思います。