Kotlinでgoogle-cloudライブラリを扱う

まず、公式で公開されているJavaのクライアントライブラリはこちらです.  
[GoogleCloudPlatform/google-cloud-java](https://github.com/GoogleCloudPlatform/google-cloud-java)  

## サービスアカウントの認証を用いて実行する
GCPサービスを利用する場合, 認証周りはデフォルトでは `gcloud auth application-default login` や `GOOGLE_APPLICATION_CREDENTIALS` をマシン側で設定する必要があります。  

その場合のサービスの利用は例えばBigQueryサービスの場合, このようなAPIが提供されています。  

```kotlin
import com.google.cloud.bigquery.BigQueryOptions

fun main() {
    val bigquery = BigQueryOptions.getDefaultInstance().getService()
}
```

ただ、プロジェクトメンバー内で共通のサービスアカウントで認証を通したいことがあります。  
コード内からサービスアカウントのjson key fileを読み込む方法を共有します。  

```kotlin
import com.google.auth.oauth2.GoogleCredentials
import com.google.auth.oauth2.ServiceAccountCredentials

object Misc {
    // サービスアカウントのCredentialsをロードする
    fun loadCredentials(account_path: String): GoogleCredentials {
        // Google認証ファイルの読み込み
        val credentials = FileInputStream(account_path).use {
            ServiceAccountCredentials
                .fromStream(it)
        }
        return credentials
    }
}
```

json key fileを読み込み `GoogleCredencials` クラスのオブジェクトとして保持できます。  

Optionsに適用する書き方は,  

```kotlin
import com.google.cloud.bigquery.BigQueryOptions

fun main() {
    val credentials = Misc.loadCredentials("<SERVICE_ACCOUNT_PATH>")
    val options = BigQueryOptions.newBuilder().setCredentials(credentials).build()
}
```

そして `BigQueryOptions#getDefaultInstance()` の代わりに生成したoptionsオブジェクトからそのまま `getService()` が実行できます.  

```kotlin
fun main() {
  val bigqury = options.getService()
}
```

## Builder Pattern
これらを踏まえて, サービスアカウントによる認証をOptional(任意)とし,  
サービスアカウントによるサービスの利用もしくはそれをしない場合はDefaultによる利用といった操作が可能な設計をします.  

```kotlin
import com.google.auth.oauth2.GoogleCredentials
import com.google.cloud.bigquery.BigQueryOptions
import com.google.cloud.bigquery.QueryJobConfiguration

class BigQueryIO(options: BigQueryOptions?) {
    val options = options

    private constructor(builder: Builder): this(builder.options)
    class Builder() {
        var options: BigQueryOptions? = null
            private set

        fun setCredentials(credentials: GoogleCredentials) = apply {
            this.options = BigQueryOptions.newBuilder().setCredentials(credentials).build()
        }
        fun build() = BigQueryIO(this)
    }

    fun runQuery(sql: String) {
        val bigquery = options?.getService() ?: BigQueryOptions.getDefaultInstance().getService()
        val queryConfig = QueryJobConfiguration.newBuilder(sql).build()
        // run Query
        val result = bigquery.query(queryConfig)
        // ...
    }
}
```

このクラスの使い方は以下です.  

```kotlin
fun main() {
    // サービスアカウント認証による利用
    val credentials = Misc.loadCredentials("<SERVICE_ACCOUNT_PATH>")
    val bq = BigQueryIO.Builder().setCredentials(credentials).build()
    // default認証による利用
    val bq = BigQueryIO.Builder().build()

    // SQLを実行
    bq.runQuery(sql)
}
```

`options` をOptionalにすることで, Nullの場合に `options?.getService()` はエルビス演算子(?:)の左の式を評価します.  

このBuilder patternだとあんまり意味ないですが, 例えばDatastoreIOを実装する際に,  
`ProjectId` と `kind` の初期化をrequired(必須)とする場合はこうです.  

```kotlin
import com.google.auth.oauth2.GoogleCredentials

import com.google.cloud.datastore.DatastoreOptions

class DatastoreIO(projectId: String, kind: String, options: DatastoreOptions?) {
    val projectId = projectId
    val kind = kind
    val options = options

    private constructor(builder: Builder): this(builder.projectId, builder.kind, builder.options) 
    class Builder(
        val projectId: String, val kind: String
    ) {
        val options: DatastoreOptions? = null
            private set

        fun setCredentials(credentials: GoogleCredentials) = apply {
            this.options = DatastoreOptions.newBuilder().setCredentials(credentials).build()
        }
        fun build() = DatastoreIO(this)
    }
}
```

使い方はこうです.  

```kotlin
fun main() {
    // サービスアカウント認証による利用
    val projectId = "<PROJECT_ID>"
    val kind = "<KIND>"
    val credentials = Misc.loadCredentials("<SERVICE_ACCOUNT_PATH>")
    val bq = DatastoreIO.Builder(projectId, kind).setCredentials(credentials).build()
}
```