diff options
author | vlad <vlad@driver.xyz> | 2017-06-13 10:25:55 -0700 |
---|---|---|
committer | vlad <vlad@driver.xyz> | 2017-06-13 10:25:55 -0700 |
commit | 0000a65ab4479a2a40e2d6468036438e9705b4aa (patch) | |
tree | 60c868828741e7e5367aa7b6d167abbdaf91d5b8 /src/main/scala/xyz/driver/common/utils | |
download | rest-query-0000a65ab4479a2a40e2d6468036438e9705b4aa.tar.gz rest-query-0000a65ab4479a2a40e2d6468036438e9705b4aa.tar.bz2 rest-query-0000a65ab4479a2a40e2d6468036438e9705b4aa.zip |
Initial extraction of Driver non-specific utilities
Diffstat (limited to 'src/main/scala/xyz/driver/common/utils')
8 files changed, 263 insertions, 0 deletions
diff --git a/src/main/scala/xyz/driver/common/utils/Computation.scala b/src/main/scala/xyz/driver/common/utils/Computation.scala new file mode 100644 index 0000000..a435afe --- /dev/null +++ b/src/main/scala/xyz/driver/common/utils/Computation.scala @@ -0,0 +1,110 @@ +package xyz.driver.common.utils + +import scala.concurrent.{ExecutionContext, Future} + +/** + * Takes care of computations + * + * Success(either) - the computation will be continued. + * Failure(error) - the computation was failed with unhandled error. + * + * Either[Result, T]: + * Left(result) is a final and handled result, another computations (map, flatMap) will be ignored. + * Right(T) is a current result. Functions in map/flatMap will continue the computation. + * + * Example: + * {{{ + * import scala.concurrent.ExecutionContext.Implicits.global + * import scala.concurrent.{ExecutionContext, Future} + * import com.drivergrp.server.com.drivergrp.server.common.utils.Computation + * + * def successful = for { + * x <- Computation.continue(1) + * y <- Computation.continue(2) + * } yield s"\$x + \$y" + * + * // Prints "Success(1 + 2)" + * successful.join.onComplete(print) + * + * def failed = for { + * x <- Computation.abort("Failed on x") + * _ = print("Second step") + * y <- Computation.continue(2) + * } yield s"\$x + \$y" + * + * // Prints "Success(Failed on x)" + * failed.join.onComplete(print) + * }}} + * + * TODO: Make future private + * + * @param future The final flow in a future. + * @tparam R Type of result for aborted computation. + * @tparam T Type of result for continued computation. + */ +final case class Computation[+R, +T](future: Future[Either[R, T]]) { + + def flatMap[R2, T2](f: T => Computation[R2, T2])(implicit ec: ExecutionContext, ev: R <:< R2): Computation[R2, T2] = { + Computation(future.flatMap { + case Left(x) => Future.successful(Left(x)) + case Right(x) => f(x).future + }) + } + + def processExceptions[R2](f: PartialFunction[Throwable, R2]) + (implicit ev1: R <:< R2, + ec: ExecutionContext): Computation[R2, T] = { + val strategy = f.andThen(x => Left(x): Either[R2, T]) + val castedFuture: Future[Either[R2, T]] = future.map { + case Left(x) => Left(x) + case Right(x) => Right(x) + } + Computation(castedFuture.recover(strategy)) + } + + def map[T2](f: T => T2)(implicit ec: ExecutionContext): Computation[R, T2] = flatMap { a => + Computation.continue(f(a)) + } + + def andThen(f: T => Any)(implicit ec: ExecutionContext): Computation[R, T] = map { a => + f(a) + a + } + + def filter(f: T => Boolean)(implicit ec: ExecutionContext): Computation[R, T] = map { a => + if (f(a)) a + else throw new NoSuchElementException("When filtering") + } + + def withFilter(f: T => Boolean)(implicit ec: ExecutionContext): Computation[R, T] = filter(f) + + def foreach[T2](f: T => T2)(implicit ec: ExecutionContext): Unit = future.foreach { + case Right(x) => f(x) + case _ => + } + + def toFuture[R2](resultFormatter: T => R2)(implicit ec: ExecutionContext, ev: R <:< R2): Future[R2] = future.map { + case Left(x) => x + case Right(x) => resultFormatter(x) + } + + def toFuture[R2](implicit ec: ExecutionContext, ev1: R <:< R2, ev2: T <:< R2): Future[R2] = future.map { + case Left(x) => x + case Right(x) => x + } + +} + +object Computation { + + def continue[T](x: T): Computation[Nothing, T] = Computation(Future.successful(Right(x))) + + def abort[R](result: R): Computation[R, Nothing] = Computation(Future.successful(Left(result))) + + def fail(exception: Throwable): Computation[Nothing, Nothing] = Computation(Future.failed(exception)) + + def fromFuture[T](input: Future[T])(implicit ec: ExecutionContext): Computation[Nothing, T] = Computation { + input.map { x => Right(x) } + } + +} diff --git a/src/main/scala/xyz/driver/common/utils/FutureUtils.scala b/src/main/scala/xyz/driver/common/utils/FutureUtils.scala new file mode 100644 index 0000000..6933c63 --- /dev/null +++ b/src/main/scala/xyz/driver/common/utils/FutureUtils.scala @@ -0,0 +1,19 @@ +package xyz.driver.common.utils + +import scala.concurrent.{ExecutionContext, Future} +import scala.util.Try + +object FutureUtils { + + def executeSynchronously[T](f: ExecutionContext => Future[T]): Try[T] = { + val future = f { + new ExecutionContext { + override def reportFailure(cause: Throwable): Unit = cause.printStackTrace() + + override def execute(runnable: Runnable): Unit = runnable.run() + } + } + future.value.get + } + +} diff --git a/src/main/scala/xyz/driver/common/utils/Implicits.scala b/src/main/scala/xyz/driver/common/utils/Implicits.scala new file mode 100644 index 0000000..bdccaac --- /dev/null +++ b/src/main/scala/xyz/driver/common/utils/Implicits.scala @@ -0,0 +1,22 @@ +package xyz.driver.common.utils + +import scala.collection.generic.CanBuildFrom + +object Implicits { + + final class ConditionalAppend[U, T[U] <: TraversableOnce[U]](val c: T[U]) extends AnyVal { + def condAppend(cond: => Boolean, value: U)(implicit cbf: CanBuildFrom[T[U], U, T[U]]): T[U] = { + val col = cbf() + if (cond) { + ((col ++= c) += value).result + } else { + c.asInstanceOf[T[U]] + } + } + } + + implicit def traversableConditionalAppend[U, T[U] <: TraversableOnce[U]](c: T[U]): ConditionalAppend[U, T] = + new ConditionalAppend[U, T](c) + + implicit def toMapOps[K, V](x: Map[K, V]): MapOps[K, V] = new MapOps(x) +} diff --git a/src/main/scala/xyz/driver/common/utils/JsonSerializer.scala b/src/main/scala/xyz/driver/common/utils/JsonSerializer.scala new file mode 100644 index 0000000..936a983 --- /dev/null +++ b/src/main/scala/xyz/driver/common/utils/JsonSerializer.scala @@ -0,0 +1,27 @@ +package xyz.driver.common.utils + +import java.text.SimpleDateFormat + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.scala.DefaultScalaModule +import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper + +object JsonSerializer { + + private val mapper = new ObjectMapper() with ScalaObjectMapper + mapper.registerModule(DefaultScalaModule) + mapper.registerModule(new JavaTimeModule) + mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")) + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL) + + def serialize(value: Any): String = { + mapper.writeValueAsString(value) + } + + def deserialize[T](value: String)(implicit m: Manifest[T]): T = { + mapper.readValue(value) + } + +} diff --git a/src/main/scala/xyz/driver/common/utils/MapOps.scala b/src/main/scala/xyz/driver/common/utils/MapOps.scala new file mode 100644 index 0000000..759fdea --- /dev/null +++ b/src/main/scala/xyz/driver/common/utils/MapOps.scala @@ -0,0 +1,10 @@ +package xyz.driver.common.utils + +final class MapOps[K, V](val self: Map[K, V]) extends AnyVal { + def swap: Map[V, Set[K]] = { + self + .toList + .groupBy { case (_, v) => v } + .mapValues(_.map { case (k, _) => k }.toSet) + } +} diff --git a/src/main/scala/xyz/driver/common/utils/RandomUtils.scala b/src/main/scala/xyz/driver/common/utils/RandomUtils.scala new file mode 100644 index 0000000..d4cd4dc --- /dev/null +++ b/src/main/scala/xyz/driver/common/utils/RandomUtils.scala @@ -0,0 +1,20 @@ +package xyz.driver.common.utils + +import java.util.concurrent.ThreadLocalRandom + +import scala.collection._ + +object RandomUtils { + + private def Random = ThreadLocalRandom.current() + + private val chars: Seq[Char] = ('0' to '9') ++ ('a' to 'z') + + def randomString(len: Int): String = { + (0 until len).map({ _ => + val i = Random.nextInt(0, chars.size) + chars(i) + })(breakOut) + } + +} diff --git a/src/main/scala/xyz/driver/common/utils/ServiceUtils.scala b/src/main/scala/xyz/driver/common/utils/ServiceUtils.scala new file mode 100644 index 0000000..dd309fb --- /dev/null +++ b/src/main/scala/xyz/driver/common/utils/ServiceUtils.scala @@ -0,0 +1,32 @@ +package xyz.driver.common.utils + +import xyz.driver.common.db.SearchFilterBinaryOperation.Eq +import xyz.driver.common.db.SearchFilterExpr +import xyz.driver.common.db.SearchFilterExpr.{Atom, Dimension} +import xyz.driver.common.logging._ + +import scala.util.{Failure, Success, Try} + + +object ServiceUtils extends PhiLogging { + def findEqFilter(filter: SearchFilterExpr, fieldName: String): Option[SearchFilterExpr] = { + findEqFilter(filter, Dimension(None, fieldName)) + } + + def findEqFilter(filter: SearchFilterExpr, dimension: Dimension): Option[SearchFilterExpr] = { + filter.find { + case Atom.Binary(dimension, Eq, _) => true + case _ => false + } + } + + def convertIdInFilterToLong(value: AnyRef): Option[Long] = { + Try(value.toString.toLong) match { + case Success(id) => + Option(id) + case Failure(e) => + logger.error(phi"Incorrect id format in filter $e") + None + } + } +} diff --git a/src/main/scala/xyz/driver/common/utils/Utils.scala b/src/main/scala/xyz/driver/common/utils/Utils.scala new file mode 100644 index 0000000..39f1294 --- /dev/null +++ b/src/main/scala/xyz/driver/common/utils/Utils.scala @@ -0,0 +1,23 @@ +package xyz.driver.common.utils + +import java.time.LocalDateTime + +object Utils { + + implicit val localDateTimeOrdering: Ordering[LocalDateTime] = Ordering.fromLessThan(_ isBefore _) + + /** + * Hack to avoid scala compiler bug with getSimpleName + * @see https://issues.scala-lang.org/browse/SI-2034 + */ + def getClassSimpleName(klass: Class[_]): String = { + try { + klass.getSimpleName + } catch { + case _: InternalError => + val fullName = klass.getName.stripSuffix("$") + val fullClassName = fullName.substring(fullName.lastIndexOf(".") + 1) + fullClassName.substring(fullClassName.lastIndexOf("$") + 1) + } + } +} |