Skip to content

Instantly share code, notes, and snippets.

@miguel-vila
Created June 19, 2020 11:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miguel-vila/cf006d103e0c279553bc7550477f4599 to your computer and use it in GitHub Desktop.
Save miguel-vila/cf006d103e0c279553bc7550477f4599 to your computer and use it in GitHub Desktop.
package com.disneystreaming.subscription.v2.infra.clients.pes
import cats.data.NonEmptyList
import com.disneystreaming.subscription.v2.core.errors.{
ProductDetailsError,
ProductRetrievalFailed
}
import com.disneystreaming.subscription.v2.infra.clients.PriceEngineServiceClient
import com.disneystreaming.subscription.v2.infra.clients.model.pes.OrderItem
import scalacache.Cache
import scalacache.caffeine.CaffeineCache
import scalacache.memoization.memoizeF
import zio.ZIO
import scala.concurrent.duration._
import scala.util.control.NonFatal
final case class CachedPriceEngineServiceClient[R](
underlying: PriceEngineServiceClient[R],
defaultTtl: FiniteDuration
) extends PriceEngineServiceClient[R] {
private implicit val orderItemCache: Cache[NonEmptyList[OrderItem]] =
CaffeineCache[NonEmptyList[OrderItem]]
case class WrappedProductDetailsException private (error: ProductDetailsError)
extends Exception
import com.disneystreaming.subscription.v2.commons.utils.ZioEffect.modes._
override def getOrderItems(
skus: NonEmptyList[String],
campaignCode: String,
voucherCode: String,
transactionCount: Long
): ZIO[
R,
ProductDetailsError,
NonEmptyList[
OrderItem
]
] = {
for {
resources <- ZIO.environment[R]
result <- memoizeF(Some(defaultTtl)) {
underlying
.getOrderItems(
skus,
campaignCode,
voucherCode,
transactionCount
) // Scaffeine's mode for ZIO works with throwable, so wrapped around throwable
.mapError(err => WrappedProductDetailsException(err): Throwable)
.provide(resources)
}.mapError {
case wrappedException: WrappedProductDetailsException =>
wrappedException.error
case NonFatal(other) => ProductRetrievalFailed(other)
}
} yield result
}
}
package com.disneystreaming.subscription.v2.infra.clients.pes
import cats.data.NonEmptyList
import com.disneystreaming.subscription.v2.core.errors.{
ProductDetailsError,
ProductRetrievalFailed
}
import com.disneystreaming.subscription.v2.infra.clients.PriceEngineServiceClient
import com.disneystreaming.subscription.v2.infra.clients.model.pes.OrderItem
import scalacache.caffeine.CaffeineCache
import scalacache.memoization.{
MemoizationConfig,
MethodCallToStringConverter,
cacheKeyExclude,
memoizeF
}
import scalacache.{Cache, CacheConfig}
import zio.{Task, ZIO}
import scala.concurrent.duration._
import scala.util.control.NonFatal
final case class CachedPriceEngineServiceClient[R](
underlying: PriceEngineServiceClient[R],
defaultTtl: FiniteDuration
) extends PriceEngineServiceClient[R] {
implicit val cacheConfig = CacheConfig(
memoization = MemoizationConfig(new MethodCallToStringConverter {
override def toString(fullClassName: String,
constructorParamss: IndexedSeq[IndexedSeq[Any]],
methodName: String,
paramss: IndexedSeq[IndexedSeq[Any]]): String = {
val key = MethodCallToStringConverter.excludeClassConstructorParams
.toString(fullClassName, constructorParamss, methodName, paramss)
println(s"PARAMS = ${paramss}")
println(s"constructorParamss = ${constructorParamss}")
println(s"KEY =$key")
key
}
})
)
private implicit val orderItemCache: Cache[NonEmptyList[OrderItem]] =
CaffeineCache[NonEmptyList[OrderItem]]
case class WrappedProductDetailsException private (error: ProductDetailsError)
extends Exception
import com.disneystreaming.subscription.v2.commons.utils.ZioEffect.modes._
override def getOrderItems(
skus: NonEmptyList[String],
campaignCode: String,
voucherCode: String,
transactionCount: Long
): ZIO[
R,
ProductDetailsError,
NonEmptyList[
OrderItem
]
] = {
for {
resources <- ZIO.environment[R]
result <- cache(skus,
campaignCode,
voucherCode,
transactionCount,
resources).mapError {
case wrappedException: WrappedProductDetailsException =>
wrappedException.error
case NonFatal(other) => ProductRetrievalFailed(other)
}
} yield result
}
def cache(
skus: NonEmptyList[String],
campaignCode: String,
voucherCode: String,
transactionCount: Long,
@cacheKeyExclude resources: R
): Task[NonEmptyList[OrderItem]] = {
memoizeF(Some(defaultTtl)) {
underlying
.getOrderItems(
skus,
campaignCode,
voucherCode,
transactionCount
)
.mapError(err => WrappedProductDetailsException(err): Throwable)
.provide(resources)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment