Skip to content

Instantly share code, notes, and snippets.

@jamesanto
Created July 23, 2019 13:07
Show Gist options
  • Save jamesanto/58b44fd630b502e95c8cccaadc27854d to your computer and use it in GitHub Desktop.
Save jamesanto/58b44fd630b502e95c8cccaadc27854d to your computer and use it in GitHub Desktop.
Generic Services & Filters based on Finagle, using ZIO
import org.scalatest.{FreeSpec, Matchers}
class Filter2FilterTest extends FreeSpec with Matchers with ZioTestSupport {
case class JsonErr()
case class JsonReq()
case class JsonRes()
case class HttpErr()
case class HttpReq()
case class HttpRes()
case class DomainErr()
case class DomainReq()
case class DomainRes()
val http: Service[HttpErr, HttpReq, HttpRes] = Service.fromFunction { httpReq =>
HttpRes()
}
val json: Filter[HttpErr, JsonErr, HttpReq, HttpRes, JsonReq, JsonRes] = { httpService =>
Service.fromFunctionM { jsonReq =>
//convert json req to http req
httpService
.provide(HttpReq())
.map { httpRes =>
JsonRes()
}
.mapError { httpErr =>
JsonErr()
}
}
}
val domain: Filter[JsonErr, DomainErr, JsonReq, JsonRes, DomainReq, DomainRes] = { jsonService =>
Service.fromFunctionM { domainReq =>
//convert domain req to json req
jsonService
.provide(JsonReq())
.map { jsonRes =>
DomainRes()
}
.mapError { jsonErr =>
DomainErr()
}
}
}
val filterStack: Service[HttpErr, HttpReq, HttpRes] => Service[DomainErr, DomainReq, DomainRes] = json >>> domain
val fullStack: Service[DomainErr, DomainReq, DomainRes] = filterStack >>> http
"should work properly" in {
val result = eval(fullStack.provide(DomainReq()))
result shouldBe Right(DomainRes())
}
}
import org.scalatest.{FreeSpec, Matchers}
class Filter2ServiceTest extends FreeSpec with Matchers with ZioTestSupport {
case class HttpErr()
case class HttpReq()
case class HttpRes()
case class DomainErr()
case class DomainReq()
case class DomainRes()
val filter: Filter[HttpErr, DomainErr, HttpReq, HttpRes, DomainReq, DomainRes] = { httpService =>
Service.fromFunctionM { domainReq =>
//convert domain req to http req
httpService
.provide(HttpReq())
.map { httpRes =>
DomainRes()
}
.mapError { httpErr =>
DomainErr()
}
}
}
val service: Service[HttpErr, HttpReq, HttpRes] = Service.fromFunction { httpReq =>
//use http client here
HttpRes()
}
val stack: Service[DomainErr, DomainReq, DomainRes] = filter >>> service
"should work properly" in {
val result = eval(stack.provide(DomainReq()))
result shouldBe Right(DomainRes())
}
}
import zio._
package object service {
type Service[+Err, -Req, +Rep] = ZIO[Req, Err, Rep]
val Service: ZIO.type = ZIO
type Filter[InErr, OutErr, InReq, InRep, OutReq, OutRep] =
Service[InErr, InReq, InRep] => Service[OutErr, OutReq, OutRep]
type SimpleFilter0[InErr, OutErr, Req, Rep] = Filter[InErr, OutErr, Req, Rep, Req, Rep]
type SimpleFilter[Err, Req, Rep] = SimpleFilter0[Err, Err, Req, Rep]
implicit class RichFunction[-A, +B](val f: A => B) extends AnyVal {
def >>>(a: A): B = f(a)
def >>>[C](g: B => C): A => C = g.compose(f)
}
}
import org.scalatest.{FreeSpec, Matchers}
class Service2ServiceTest extends FreeSpec with Matchers with ZioTestSupport {
case class Err1()
case class Err2()
private val toInt: Service[Err1, String, Int] = Service.fromFunction(_.toInt)
private val toDouble: Service[Err2, Int, Double] = Service.fromFunction(_.toDouble)
private val failWithErr1: Service[Err1, String, Int] = Service.fail(Err1())
private val failWithErr2: Service[Err2, Int, Double] = Service.fail(Err2())
"should work properly" in {
val service = toInt >>> toDouble
val result = eval(service.provide("12"))
result shouldBe Right(12.0)
}
"should fail when first service fails" in {
val service = failWithErr1 >>> toDouble
val result = eval(service.provide("12"))
result shouldBe Left(Err1())
}
"should fail when second service fails" in {
val service = toInt >>> failWithErr2
val result = eval(service.provide("12"))
result shouldBe Left(Err2())
}
"should fail with first error when both service fail" in {
val service = failWithErr1 >>> failWithErr2
val result = eval(service.provide("12"))
result shouldBe Left(Err1())
}
}
import zio.{DefaultRuntime, IO}
trait ZioTestSupport {
private val rt = new DefaultRuntime {}
def eval[E, A](io: IO[E, A]): Either[E, A] = {
rt.unsafeRun(io.either)
}
}
@jamesanto
Copy link
Author

jamesanto commented Jul 23, 2019

Refer this issue for details : zio/zio#1214

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment