Slide 1

Slide 1 text

Swagger Codegenで APIクライアントgem 自動生成 2017-09-28 どこでもKotlin #2 @Speee 滝安純平 (@juntaki)

Slide 2

Slide 2 text

自己紹介 滝安 純平 (@juntaki) エムスリー株式会社エンジニア (2017/8-) 前職はファイルシステムの人 https://juntaki.com

Slide 3

Slide 3 text

もくじ 1. gemを自動生成したい背景 2. Swagger Codegenの仕組みと使い方 3. Swagger Codegen w/ Springfoxのハマりポイント 4. Springfennecの紹介

Slide 4

Slide 4 text

gemを自動生成したい背景

Slide 5

Slide 5 text

サーバーサイドKotlinでリニューアル! サーバーサイド(API) フロント API呼び出し API呼び出しをどう実装するか?

Slide 6

Slide 6 text

まじめに全部実装すると 1. Kotlinで新しいAPIを作る 2. APIクライアントgemを作る 3. Railsでフロントを作る gemはHTTPの呼び出しをWrapしているだけ

Slide 7

Slide 7 text

gemは自動生成できる 1. Kotlinで新しいAPIを作る 2. Swagger Codegenでgem自動生成 3. Railsでフロントを作る 重要な実装に注力できる!

Slide 8

Slide 8 text

Swagger Codegenの仕組みと使い方

Slide 9

Slide 9 text

Swagger https://swagger.io/

Slide 10

Slide 10 text

Swagger Tools Swagger Definition(spec file)を生成・パースするツール群 Swagger definition (spec file) API ドキュメント サーバ スタブ サーバ実装 クライアント ライブラリ Swagger Core Swagger UI Swagger Codegen Swagger Editor 手入力 今回説明したいのは下半分のフロー

Slide 11

Slide 11 text

Spring bootと使うには? Spring Boot サーバ実装 クライアント ライブラリ Swagger Core Swagger Codegen Springfox Springの実装とSwagger Coreを使い、spec fileを生成する Swagger definition (spec file)

Slide 12

Slide 12 text

Docket = APIのグループを定義して、タイトルや属性などを追加 Springfoxの使い方 @EnableSwagger2 class SpringfoxConfig { @Bean fun demoApiDocument(): Docket { return Docket(DocumentationType.SWAGGER_2) .groupName("springfox") .select() .paths(PathSelectors.ant("/demo/**")) .build() } ...

Slide 13

Slide 13 text

Ruby gemを生成するには? ruby用テンプレート デフォルトで含まれているテンプレートを使うか mustacheで独自テンプレートを作成する Spring Boot サーバ実装 クライアント gem Swagger Core Swagger Codegen Springfox Swagger definition (spec file)

Slide 14

Slide 14 text

config.json 独自テンプレートを使う場合 コマンド例 java -jar swagger-codegen-cli.jar generate \ --api-package package-name \ -o /path/to/outputdir \ -l ruby \ -i http://localhost:8080/v2/api-docs?group=target-api \ -c config.json \ -t /path/to/template { "gemName": "api-client", "moduleName": "ApiClient", "gemVersion": "1.0.0" }

Slide 15

Slide 15 text

Swagger Codegen w/ Springfoxの ハマりポイント

Slide 16

Slide 16 text

ハマりポイント:もくじ SpringfoxはAPI自動生成のことを考えていない(ように見える) 1. OperationIdにsuffixがつく 2. Nested Class名が重複すると静かに壊れる 3. spec fileがサーバを起動しないと取り出せない

Slide 17

Slide 17 text

OperationIdにsuffixがつく OperationId:クライアントgemの関数名になるパラメータ ● 複数Docketから参照されている ● 別クラスにが同名の定義ある Kotlin spec file "paths": { "/demo/user/{id}": { "get": { "tags": [ "demo-controller" ], "summary": "getUser", "operationId": "getUserUsingGET_1", … } @RestController @RequestMapping("/demo") class DemoController { @GetMapping("/user/{id}") fun getUser(@PathVariable id: Int): userApiParam { … }

Slide 18

Slide 18 text

対策:OperationIdにsuffixがつく 1. ユニークなIDになるようにアノテーションをつける  @ApiOperaitonのnicknameが設定してあれば、使われる 2. 出力前に削る  同一Docket内で重複したときに例外を出すなどの工夫が必要 詳細は→https://github.com/springfox/springfox/issues/1224#issuecomment-229491446 val newOperationId = it.replace(Regex("_[0-9]+$"), "")

Slide 19

Slide 19 text

Nested Class名が重複すると静かに壊れる "AdminApiParam": { "type": "object", "properties": { "info": { "$ref": "#/definitions/Info" } } }, "UserApiParam": { "type": "object", "properties": { "info": { "$ref": "#/definitions/Info" } } } class AdminApiParam( val info: Info ) { class Info( val secretInfo: String, val region: String ) } class UserApiParam( val info: Info ) { class Info(val region: String) } Kotlin spec file ビルドが通ってgemも生成されるが、動かなくなる

Slide 20

Slide 20 text

対策:Nested Class名が重複すると静かに壊れる Nested ClassにFQCNっぽいユニークな名前をつける (現状は運用でなんとかするしかない・・・) 気づけた場合は、@ApiModelでリネームも可能 詳細は→https://github.com/springfox/springfox/issues/182

Slide 21

Slide 21 text

spec fileがサーバを起動しないと取り出せない SpringfoxはBeanとして登録されるように実装されている → spec fileの生成だけを単独で動かせない 1. localhostでサーバ立ち上げ 2. サーバが起動したら、spec fileをダウンロード jsonのURLはここに書いてある

Slide 22

Slide 22 text

対策:spec fileがサーバを起動しないと取り出せない DBへのアクセスが必要な処理はすべてlazyにする DBのMockがある環境で動かす、など ユニットテストに書いて、生成する方法もある 詳細は→https://github.com/springfox/springfox/issues/2038

Slide 23

Slide 23 text

まとめ:Swagger Codegen w/ Springfox やればできるが、注意が必要 紹介したコードが含まれるサンプルプロジェクト → https://github.com/juntaki/springfennec-demo

Slide 24

Slide 24 text

サーバーサイド”Kotlin”特集とは・・? Javaでやってもだいたい同じことがおきると思います

Slide 25

Slide 25 text

Springfennecの紹介

Slide 26

Slide 26 text

Springfennec Swagger spec generator from Spring annotations, written in Kotlin https://github.com/juntaki/springfennec Springの実装とSwagger annotationから、 Annotation processorでビルド時にspec fileを生成する つくりました

Slide 27

Slide 27 text

ハマりポイントは対策済み ● OperationIdにsuffixなし、重複したらビルド失敗 ● Model名はFQCNベースなので重複しない ● Annotation processingでビルド時にspec fileを生成 APIクライアント生成に向いている すごーい

Slide 28

Slide 28 text

使い方 @ApiGroup annotationと、dependenciesを書き足す。 build.gradle Kotlin dependencies { ... // Springfennec def springfennecVersion = 'x.x.x' compile('io.swagger:swagger-core:1.5.16') compile("com.juntaki:springfennec:${springfennecVersion}") kapt("com.juntaki:springfennec:${springfennecVersion}") ... } @ApiGroup(value="^/pet/.*", // regex for path (not include basePath) name = "pet_api", // output will be spec_${name}.json, e.g. spec_pet_api.json apiInfo = @SwaggerDefinition(...))

Slide 29

Slide 29 text

springfennec-demo https://github.com/juntaki/springfennec-demo Springfoxと同時使用可能 生成されるspec fileもリポジトリに入っています

Slide 30

Slide 30 text

まとめ swagger-codegenでAPIクライアントgemを自動生成できる Springbootを使っているなら、 ● Springfox + ハマり回避策 ● Springfennec つかってみて! Follow me! https://juntaki.com