finchを試してみた

軽量finchを試してみました。2018年2月リリースのfinchを落としてきて、参考リンクのコーディングを基に動かそうとしたが、コンパイルが通らなかったので手を加えてみました。

```scala:build.sbt

libraryDependencies ++= Seq(
  "com.twitter" %% "finagle-mysql" % "18.3.0",
  "com.github.finagle" %% "finch-core" % "0.17.0",
  "com.github.finagle" %% "finch-argonaut" % "0.17.0",
  "com.twitter" %% "finagle-http" % "18.3.0",
  "com.github.finagle" %% "finch-circe" % "0.17.0",
  "io.circe" %% "circe-core" % "0.9.1",
  "io.circe" %% "circe-generic" % "0.9.1",
  "ch.qos.logback"  %  "logback-classic"  % "1.1.3"
)

```


```scala:Main.scala
package jp.co.finchitest

import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{Http, Service}
import com.twitter.util.Await
import io.circe.generic.auto._ // *
import io.finch._
import io.finch.circe._ // *
import io.finch.syntax._
import jp.co.finchitest.helpers.MysqlHelper._
import jp.co.finchitest.forms._
import jp.co.finchitest.forms.UserForm._


object Main {

  val listUser: Endpoint[Seq[User]] = get("users") {
    User.all.map{
      case Seq() => NotFound(new Exception("Record Not Found"))
      case users => Ok(users)
    }
  }

  val showUser: Endpoint[User] = get("users" :: path[Long]) {id: Long =>
    User.find(id).map {
      case None => NotFound(new Exception("Record Not Found"))
      case Some(u) => Ok(u)
    }
  }

  val createUser: Endpoint[User] = post("users" :: userReqParams) { p: UserForm =>
    (for {
      id   <- User.create(p.email, p.screen_name)
      user <- User.find(id)
    } yield user) map {
      case Some(u) => Created(u)
      case _ => NotFound(new Exception("Record Not Found"))
    }
  }

  // 配列jsonも出来るかな
  val createUserByJson: Endpoint[Int] = post("usersjson" :: userJson) { p: Seq[UserForm] =>
    val insertedId = p.map(a => User.create(a.email, a.screen_name))
    Ok(insertedId.size)
  }

  val userService: Service[Request, Response] = ( listUser :+: showUser :+: createUser :+: createUserByJson)
    .handle{
      case fe: io.finch.Error => BadRequest(new Exception(fe.getMessage)) // validation Error が起きた場合のcase
      case e: Exception => InternalServerError(new Exception(e.getMessage))
    }
    .toServiceAs[Application.Json]

  def main(args: Array[String]): Unit = {
    Await.ready(Http.serve(":8082", userService))
  }
}
```

```
なんか以下のエラーが出た。どうやら、レスポンス時にしっかりとエンコーディングしているかどうかをコンパイル時にチェッックしてくれるらしい。。
error: An Endpoint you're trying to convert into a Finagle service is missing one or more encoders

インポート文を追加したら解消された。
import io.circe.generic.auto._ // *
import io.finch.circe._ // * 
どうやら、implicit def encodeCirce を toServiceAsメソッドに暗黙渡しをしないとダメみだいだ。
```

```scala:Main.scala
  // 配列jsonも出来るかな
  val createUserByJson: Endpoint[Int] = post("usersjson" :: userJson) { p: Seq[UserForm] =>
    val insertedId = p.map(a => User.create(a.email, a.screen_name))
    Ok(insertedId.size)
  }
```

```scala:curl
// 以下のcurlコマンドで、配列jsonも登録できた。
curl -d '[{"email":"eee1@com","screen_name":"test1"},{"email":"eef@ssl","screen_name":"test223"}]' -H "Content-Type: application/json" -X POST http://localhost:8082/usersjson

```


```scala:UserForm.scala
package jp.co.finchitest.forms

import io.finch.{Endpoint, ValidationRule, param}

// formにemail validation機能を付与してみました
case class UserForm(email: String, screen_name: String)
object UserForm {
  val mailRule = ValidationRule[String]("mail should be mailRule")(a => """(\w+)@([\w\.]+)""".r.unapplySeq(a).isDefined)
  val userReqParams:Endpoint[UserForm]= (
      param("email").should(mailRule) ::
      param("screen_name")
    ).as[UserForm]

  val userJson:Endpoint[Seq[UserForm]] = jsonBody[Seq[UserForm]]
}

```


```scala:MysqlHelper.scala
package jp.co.finchitest.helpers

import com.twitter.finagle.Mysql

//WebアプリらしくDB環境周りは独立してみました。
object MysqlHelper {

  implicit val client = Mysql.client
    .withCredentials("root", null)
    .withDatabase("nano_planner_dev")
    .newRichClient("127.0.0.1:3306")
}

```

```scala:User.scala
リンク：Finch + MySQLでREST APIサーバを構築する のコードから変えておりません
```




参考リンク：finchについての情報を一切持ってないので、参考にさせて頂きました。大変助かりました。ありがとうございます。
[Finch + MySQLでREST APIサーバを構築する](https://blog.dakatsuka.jp/2015/12/07/finch.html)
[finch触ってみた](https://qiita.com/ryoppy/items/8b02d918543dafa02e87)
[finch公式サイト](https://finagle.github.io/finch/cookbook.html)