Created
July 17, 2016 21:25
-
-
Save xasima/4d40da673e6029e2ed3f8aff7c61cbce to your computer and use it in GitHub Desktop.
circe in finch question
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import com.twitter.app.Flag | |
| import com.twitter.finagle.http.{Request, Response} | |
| import com.twitter.finagle.{Http, ListeningServer, Service} | |
| import com.twitter.server.TwitterServer | |
| import com.twitter.util.Await | |
| import io.circe.{Encoder, Json} | |
| import io.finch._ | |
| import io.finch.circe._ | |
| import io.finch.circe.dropNullKeys._ | |
| import io.circe.generic.auto._ | |
| import io.circe.syntax._ | |
| import com.twitter.finagle.http.Message | |
| import com.twitter.finagle.http.Message.ContentTypeJson | |
| import com.twitter.io.Buf._ | |
| object RestServer extends TwitterServer { | |
| // -- Model ---------------------------- | |
| sealed trait BaseModel | |
| final case class Foo(id: String) extends BaseModel | |
| final case class Bar(param: String) extends BaseModel | |
| final case class FooBar(foo: Foo, bars:List[Bar]) extends BaseModel | |
| // -- Backend Mock ---------------------------- | |
| def getFooList(a:String = "")= List(Foo("1"), Foo("2")) | |
| def getBarList(a:String = "")= List(Bar("a"), Bar("b")) | |
| def getFooBar() =FooBar(Foo("3"), List(Bar("c"),Bar("d"))) | |
| // -- Encoders ---------------------------- | |
| // QUESTION 1 - Expected to be able to use base type (BaseModel) encoder by default and | |
| // overwrite encoder just for some specific types | |
| // but this doesn't work | |
| //implicit val baseEncoder = Encoder.instance[BaseModel] { f => f.asJson } | |
| /* Error:(36, 7) not enough arguments for method asJson: | |
| (implicit encoder: io.circe.Encoder[RestServer.BaseModel])io.circe.Json. | |
| Unspecified value parameter encoder. | |
| f.asJson | |
| ^ | |
| */ | |
| // QUESTION 2 - Moreover, expected to see the build-in cicle encoder for case classes | |
| implicit val fooEncoder = Encoder.instance[Foo] { f => | |
| f.asJson | |
| } | |
| implicit val barEncoder = Encoder.instance[Bar] { f => | |
| f.asJson | |
| } | |
| implicit val foobarEncoder = Encoder.instance[FooBar] { f => | |
| f.asJson | |
| } | |
| implicit def fooResponseEncoder: EncodeResponse[Foo] = | |
| EncodeResponse(ContentTypeJson)(f => Utf8(Map("data" -> f).asJson.noSpaces)) | |
| implicit def barResponseEncoder: EncodeResponse[Bar] = | |
| EncodeResponse(ContentTypeJson)(b => Utf8(Map("data" -> b).asJson.noSpaces)) | |
| // QUESTION 3: The same, why need to explicitly declare natural encoding for case classes | |
| implicit def fooListResponseEncoder: EncodeResponse[List[Foo]] = | |
| EncodeResponse(ContentTypeJson)(fooList => Utf8(Json.arr(fooList map {_.asJson}:_*).asJson.noSpaces)) | |
| implicit def barListResponseEncoder: EncodeResponse[List[Bar]] = | |
| EncodeResponse(ContentTypeJson)(barList => Utf8(Json.arr(barList map {_.asJson}:_*).asJson.noSpaces)) | |
| // QUESTION 4: Is something may be omitted, relying on previous encoders ? | |
| // {foo: {"id":"1"}, bars: [{param:"a"}, {"param":"b"}] | |
| // | |
| implicit def fooBarResponseEncoder: EncodeResponse[FooBar] = | |
| EncodeResponse(ContentTypeJson)(f => | |
| Utf8(Map("foo" -> f.foo.asJson, | |
| "bars" -> Json.arr(f.bars map {_. asJson}: _*)).asJson.noSpaces)) | |
| // -- Endpoints ---------------------------- | |
| val fooListApi: Endpoint[List[Foo]] = get("v1" :: "foos" ) { | |
| Ok(getFooList()) | |
| } | |
| val fooApi: Endpoint[Foo] = get("v1" :: "foos" :: string ) { | |
| (a: String) => | |
| Ok(getFooList(a)(0)) | |
| } | |
| val barListApi: Endpoint[List[Bar]] = get("v1" :: "bars" ) { | |
| Ok(getBarList() ) | |
| } | |
| val barApi: Endpoint[Bar] = get("v1" :: "bars" :: string ) { | |
| (a: String) => | |
| Ok(getBarList(a)(0) ) | |
| } | |
| val foobarApi: Endpoint[FooBar] = get("v1" :: "foobar" ) { | |
| Ok(getFooBar) | |
| } | |
| // Finch-finagle service | |
| val apiService: Service[Request, Response] = | |
| (fooListApi :+: fooApi :+: barListApi :+: barApi :+: foobarApi).handle({ | |
| case e: Exception => NotFound(e) | |
| }).toService | |
| val port: Flag[Int] = flag("port", 8081, "TCP port for HTTP server") | |
| def main(): Unit = { | |
| val server: ListeningServer = Http.server.serve(s":${port()}", apiService) | |
| println("Listening: " + port() ) | |
| onExit { server.close() } | |
| Await.ready(adminHttpServer) | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks @vkostyukov! Fixed just by removing import io.finch.circe.dropNullKeys._ everywhere.
Discussion was held here https://gitter.im/finagle/finch?at=578c7988196179690eff3eca