nowで作成したAPIをKotlinとRetrofitとRxJavaとJacksonでGET/POSTする

いつもAndroidでRetrofit+RxJavaを使ってAPI通信する処理を書いてるが、今回はKotlinで挑戦してみる。
ついでに、使ったことのないnowもJacksonも触ってみた。

## 作成するもの
- nowを使ったnodejs製のAPIモック（express）
- Androidアプリ（Kotlin/Retrofit/RxJava/Jackson）


## 環境
- macOS Sierra
- kotlin_version = '1.2.21'
- Android Studio 3.0.1
- node v9.4.0

## API
### 準備
APIモックをexpressを使ったnodejsで作成し、nowにデプロイしていく。
nowについては[この記事](https://zeit.co/now#get-started)を参考にした。
以下、作業ログ

```

// APIモックのプロジェクト準備
$ mkdir try-now && cd try-now
// package.json作成
$ npm init -y
// expressを追加
$ npm i -S express
// apiの処理を書くjsを作成
$ touch index.js

// nowのインストール
$ npm i -G now
// nowのログイン（メールアドレスを入力すると確認メールが届くのでverifyする）
$ now login

```

### 実装

index.jsに以下を貼り付ける。
レスポンス変数は適当に作った。

```javascript:index.js
var express = require('express')
var app = express()

app.get('/', function (req, res) {
  const json = {
    status: 1,
    card_status: 10,
    card_type: 'normal',
    card_id: "ca1111",
  }
  res.json(json)
})

app.post('/', function (req, res) {
  const json = {
    status: 2,
    card_status: 20,
    result_code: 1
  }
  res.json(json)
})

app.listen(3000)
```
ポートは3000を指定してるけど、なくても良い。

これでAPIモックはできあがった。

```
$ node index.js
```

としてローカルでも使えるが、今回はnowにデプロイする

### デプロイ

nowでデプロイ、超簡単

```
$ now
```
`https://try-now-kplvmhijbn.now.sh`というアドレスが表示されたので、これをAPIモックとして利用できる。

このアドレスに対してgetやpostをしていく。

## Androidアプリ
### 使用するライブラリ

app/build.gradleに以下のライブラリ達を追加する

```gradle:app/build.gradle
    // Retrofit2
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-jackson:2.3.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

    // RxJava2
    implementation "io.reactivex.rxjava2:rxjava:2.1.9"

    // RxAndroid
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'

    // Jackson
    implementation 'com.fasterxml.jackson.core:jackson-core:2.9.4'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.4'
    implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.4'
    implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.4"

```

ソース：https://github.com/ikemura23/try-now

### 実装
Retrofitのインタフェースを作成。
戻り値はRxjavaのObservableにする。

```kotlin:ApiService.kt
interface ApiService {
    @POST("/")
    fun post(): Observable<PostResponse>

    @GET("/")
    fun get(): Observable<GetResponse>
}

```
APIクライアントを作成していく。
今回、baseUrlにはnowで作成したurlを設定する。

```kotlin
class ApiClient {
    var apiService: ApiService

    init {
        val retrofit: Retrofit = Retrofit.Builder()
                .baseUrl("https://try-now-kplvmhijbn.now.sh")
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(JacksonConverterFactory.create(ObjectMapper()))
                .build()
        apiService = retrofit.create(ApiService::class.java)
    }

    /**
     * GETリクエスト
     */
    fun get(): Observable<GetResponse> =
            apiService.get()

    /**
     * POSTリクエスト
     */
    fun post(): Observable<PostResponse> =
            apiService.post()
}

```

APIクライアント呼び出しは以下
ラムダ初めて触った、すごい

```kotlin
  // get
        apiClient.post()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(
                        { res -> Log.d(TAG, res.toString()) },
                        { error -> Log.e(TAG, "{$error.message}") },
                        { Log.d(TAG, "post completed") }
                )

  // post
        apiClient.post()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(
                        { res -> Log.d(TAG, res.toString()) },
                        { error -> Log.e(TAG, "{$error.message}") },
                        { Log.d(TAG, "post completed") }
                )

```

モデルについては、
Response（共通レスポンス）を継承した各モデルが存在するイメージで作成した。

```kotlin:Response.kt
/**
 * 共通レスポンス
 */
open class Response(

        @field:JsonProperty("status") val status: Int = 0,
        @field:JsonProperty("card_status") val cardStatus: Int = 0
)
```

```kotlin:GetResponse
/**
 * GETレスポンスのモデル
 */
data class GetResponse(
        @field:JsonProperty("card_type") val cardType: String = "",
        @field:JsonProperty("card_id") val cardId: String = ""
) : Response() {
    override fun toString(): String =
            "GetResponse(status='$status' cardStatus='$cardStatus' cardType='$cardType', cardId='$cardId')"
}
```

```kotlin: PostResponse
/**
 * POSTレスポンスのモデル
 */
data class PostResponse(
        @field:JsonProperty("card_type") val cardType: String = "",
        @field:JsonProperty("card_id") val cardId: String = "",
        @field:JsonProperty("result_code") val resultCode: Int = 0
) : Response() {
    override fun toString(): String =
            "PostResponse(status='$status' cardStatus='$cardStatus' resultCode='$resultCode')"
}
```

この継承してるあたり、もうちょいうまくできないだろうか。

ビルドして実際に動作させると、Logcatに以下のログが表示されていればGET/POSTが成功している

```logcat
getのログ
D/AppCompatActivity: GetResponse(status='1' cardStatus='10' cardType='normal', cardId='ca1111')
D/AppCompatActivity: get completed

postのログ
D/AppCompatActivity: PostResponse(status='2' cardStatus='20' resultCode='1')
D/AppCompatActivity: post completed
```

ソース：https://github.com/ikemura23/Kotlin-Jackson-Sample

## ハマったところ
#### Jacksonが@JsonPropertyを認識してくれない
APIレスポンスをJacksonでシリアライズする際、共通レスポンスモデルであるResponse.ktがKotlin用のアノテーションでないとエラーになる。

Jacksonがエラーが継承元の変数を@JsonPropertyだと認識してくれなかった。
継承先は@JsonPropertyでも大丈夫なのに…

NG : `@JsonProperty`
OK : `@field:JsonProperty`

実際のコード

```kotlin:Response.kt
/**
 * 共通レスポンス
 */
open class Response(
        // NG
        //@JsonProperty("status") val status: Int = 0,
        //@JsonProperty("card_status") val cardStatus: Int = 0

        // OK
        @field:JsonProperty("status") val status: Int = 0,
        @field:JsonProperty("card_status") val cardStatus: Int = 0
)
```

同様の問題が起きてないか調べたところ、以下のリンクに辿り着き、kotlinアノテーションを使うことで解決できた。
- https://github.com/FasterXML/jackson-module-kotlin/issues/56#issuecomment-321932309
- https://github.com/FasterXML/jackson-module-kotlin/issues/98#issuecomment-352016501

## 感想
nowが楽すぎてすごい

kotlinだとJavaよりコード量少なくて楽で良い