aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/pdsuicommon
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuicommon')
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/concurrent/BridgeUploadQueueRepositoryAdapter.scala140
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/DbCommand.scala15
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/DbCommandFactory.scala14
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/EntityExtractorDerivation.scala66
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/FakeDbIo.scala9
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/MySqlContext.scala90
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/MysqlQueryBuilder.scala88
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala5
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/PostgresQueryBuilder.scala108
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/QueryBuilder.scala340
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala2
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/repositories/BridgeUploadQueueRepository.scala21
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala1
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala15
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala78
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala11
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/json/JsResultOps.scala15
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/json/JsonValidationException.scala5
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/json/Serialization.scala46
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/serialization/PlayJsonSupport.scala34
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/utils/CustomSwaggerJsonFormats.scala62
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/utils/Implicits.scala8
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/utils/WriteableImplicits.scala17
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/utils/WritesUtils.scala21
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/validation/AdditionalConstraints.scala33
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/validation/package.scala8
26 files changed, 74 insertions, 1178 deletions
diff --git a/src/main/scala/xyz/driver/pdsuicommon/concurrent/BridgeUploadQueueRepositoryAdapter.scala b/src/main/scala/xyz/driver/pdsuicommon/concurrent/BridgeUploadQueueRepositoryAdapter.scala
deleted file mode 100644
index 3bf9192..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/concurrent/BridgeUploadQueueRepositoryAdapter.scala
+++ /dev/null
@@ -1,140 +0,0 @@
-package xyz.driver.pdsuicommon.concurrent
-
-import java.time.LocalDateTime
-import java.time.temporal.ChronoUnit
-
-import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueue.Item
-import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueueRepositoryAdapter.Strategy
-import xyz.driver.pdsuicommon.db._
-import xyz.driver.pdsuicommon.db.repositories.BridgeUploadQueueRepository
-import xyz.driver.pdsuicommon.logging._
-
-import scala.concurrent.duration.{Duration, FiniteDuration}
-import scala.concurrent.{ExecutionContext, Future}
-import scala.util.Try
-
-object BridgeUploadQueueRepositoryAdapter {
-
- /**
- * Defines how we work with queue, when an user attempts to remove/tryRetry an item.
- */
- sealed trait Strategy {
-
- def onComplete: Strategy.OnComplete
-
- def on(attempt: Int): Strategy.OnAttempt
-
- }
-
- object Strategy {
-
- /**
- * Works forever, but has a limit for intervals.
- */
- final case class LimitExponential(startInterval: FiniteDuration,
- intervalFactor: Double,
- maxInterval: FiniteDuration,
- onComplete: OnComplete)
- extends Strategy {
-
- override def on(attempt: Int): OnAttempt = {
- OnAttempt.Continue(intervalFor(attempt).min(maxInterval))
- }
-
- private def intervalFor(attempt: Int): Duration = {
- Try(startInterval * Math.pow(intervalFactor, attempt.toDouble))
- .getOrElse(maxInterval)
- }
- }
-
- /**
- * Used only in tests.
- */
- final case class Stop(onComplete: OnComplete = OnComplete.Delete) extends Strategy {
-
- override def on(attempt: Int) = OnAttempt.Complete
-
- }
-
- /**
- * Used only in tests.
- */
- final case class Constant(interval: FiniteDuration) extends Strategy {
-
- override val onComplete = OnComplete.Delete
-
- override def on(attempt: Int) = OnAttempt.Continue(interval)
-
- }
-
- sealed trait OnComplete
- object OnComplete {
- case object Delete extends OnComplete
- case object Mark extends OnComplete
-
- implicit def toPhiString(x: OnAttempt): PhiString = Unsafe(x.toString)
- }
-
- sealed trait OnAttempt
- object OnAttempt {
- case object Complete extends OnAttempt
- final case class Continue(interval: Duration) extends OnAttempt
-
- implicit def toPhiString(x: OnAttempt): PhiString = Unsafe(x.toString)
- }
- }
-}
-
-class BridgeUploadQueueRepositoryAdapter(strategy: Strategy, repository: BridgeUploadQueueRepository, dbIo: DbIo)(
- implicit executionContext: ExecutionContext)
- extends BridgeUploadQueue with PhiLogging {
-
- override def add(item: Item): Future[Item] = dbIo.runAsync(repository.add(item))
-
- override def get(kind: String): Future[Option[Item]] = dbIo.runAsync(repository.getOne(kind))
-
- override def complete(kind: String, tag: String): Future[Unit] = {
- import Strategy.OnComplete._
-
- strategy.onComplete match {
- case Delete => dbIo.runAsync(repository.delete(kind, tag))
- case Mark =>
- dbIo.runAsyncTx {
- repository.getById(kind, tag) match {
- case Some(x) => repository.update(x.copy(completed = true))
- case None => throw new RuntimeException(s"Can not find the task: kind=$kind, tag=$tag")
- }
- }
- }
- }
-
- /**
- * Tries to continue the task or complete it
- */
- override def tryRetry(item: Item): Future[Option[Item]] = {
- import Strategy.OnAttempt._
-
- logger.trace(phi"tryRetry($item)")
-
- val newAttempts = item.attempts + 1
- val action = strategy.on(newAttempts)
- logger.debug(phi"Action for ${Unsafe(newAttempts)}: $action")
-
- action match {
- case Continue(newInterval) =>
- val draftItem = item.copy(
- attempts = newAttempts,
- nextAttempt = LocalDateTime.now().plus(newInterval.toMillis, ChronoUnit.MILLIS)
- )
-
- logger.debug(draftItem)
- dbIo.runAsync {
- Some(repository.update(draftItem))
- }
-
- case Complete =>
- logger.warn(phi"All attempts are out for $item, complete the task")
- complete(item.kind, item.tag).map(_ => None)
- }
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/DbCommand.scala b/src/main/scala/xyz/driver/pdsuicommon/db/DbCommand.scala
deleted file mode 100644
index 0af104e..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/DbCommand.scala
+++ /dev/null
@@ -1,15 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import scala.concurrent.Future
-
-trait DbCommand {
- def runSync(): Unit
- def runAsync(transactions: DbIo): Future[Unit]
-}
-
-object DbCommand {
- val Empty: DbCommand = new DbCommand {
- override def runSync(): Unit = {}
- override def runAsync(transactions: DbIo): Future[Unit] = Future.successful(())
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/DbCommandFactory.scala b/src/main/scala/xyz/driver/pdsuicommon/db/DbCommandFactory.scala
deleted file mode 100644
index 1e2fb74..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/DbCommandFactory.scala
+++ /dev/null
@@ -1,14 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import scala.concurrent.{ExecutionContext, Future}
-
-trait DbCommandFactory[T] {
- def createCommand(orig: T)(implicit ec: ExecutionContext): Future[DbCommand]
-}
-
-object DbCommandFactory {
- def empty[T]: DbCommandFactory[T] = new DbCommandFactory[T] {
- override def createCommand(orig: T)(implicit ec: ExecutionContext): Future[DbCommand] =
- Future.successful(DbCommand.Empty)
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/EntityExtractorDerivation.scala b/src/main/scala/xyz/driver/pdsuicommon/db/EntityExtractorDerivation.scala
deleted file mode 100644
index 02ba322..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/EntityExtractorDerivation.scala
+++ /dev/null
@@ -1,66 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.sql.ResultSet
-
-import io.getquill.NamingStrategy
-import io.getquill.dsl.EncodingDsl
-
-import scala.language.experimental.macros
-import scala.reflect.macros.blackbox
-
-trait EntityExtractorDerivation[Naming <: NamingStrategy] { this: EncodingDsl =>
-
- /*
- Simple Quill extractor derivation for [[T]]
- Only case classes available. Type parameters is not supported
- */
- def entityExtractor[T]: (ResultSet => T) = macro EntityExtractorDerivation.impl[T]
-}
-
-object EntityExtractorDerivation {
- def impl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
- import c.universe._
- val namingStrategy = c.prefix.actualType
- .baseType(c.weakTypeOf[EntityExtractorDerivation[NamingStrategy]].typeSymbol)
- .typeArgs
- .head
- .typeSymbol
- .companion
- val functionBody = {
- val tpe = weakTypeOf[T]
- val resultOpt = tpe.decls.collectFirst {
- // Find first constructor of T
- case cons: MethodSymbol if cons.isConstructor =>
- // Create param list for constructor
- val params = cons.paramLists.flatten.map { param =>
- val t = param.typeSignature
- val paramName = param.name.toString
- val col = q"$namingStrategy.column($paramName)"
- // Resolve implicit decoders (from SqlContext) and apply ResultSet for each
- val d = q"implicitly[${c.prefix}.Decoder[$t]]"
- // Minus 1 cause Quill JDBC decoders make plus one.
- // ¯\_(ツ)_/¯
- val i = q"row.findColumn($col) - 1"
- val decoderName = TermName(paramName + "Decoder")
- val valueName = TermName(paramName + "Value")
- (
- q"val $decoderName = $d",
- q"val $valueName = $decoderName($i, row)",
- valueName
- )
- }
- // Call constructor with param list
- q"""
- ..${params.map(_._1)}
- ..${params.map(_._2)}
- new $tpe(..${params.map(_._3)})
- """
- }
- resultOpt match {
- case Some(result) => result
- case None => c.abort(c.enclosingPosition, s"Can not derive extractor for $tpe. Constructor not found.")
- }
- }
- q"(row: java.sql.ResultSet) => $functionBody"
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/FakeDbIo.scala b/src/main/scala/xyz/driver/pdsuicommon/db/FakeDbIo.scala
deleted file mode 100644
index ac42a34..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/FakeDbIo.scala
+++ /dev/null
@@ -1,9 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import scala.concurrent.Future
-
-object FakeDbIo extends DbIo {
- override def runAsync[T](f: => T): Future[T] = Future.successful(f)
- override def runAsyncTx[T](f: => T): Future[T] = Future.successful(f)
- override def runSyncTx[T](f: => T): Unit = f
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/MySqlContext.scala b/src/main/scala/xyz/driver/pdsuicommon/db/MySqlContext.scala
deleted file mode 100644
index 9d2664d..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/MySqlContext.scala
+++ /dev/null
@@ -1,90 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.io.Closeable
-import java.time._
-import java.util.concurrent.Executors
-import javax.sql.DataSource
-
-import com.typesafe.config.Config
-import io.getquill._
-import xyz.driver.pdsuicommon.concurrent.MdcExecutionContext
-import xyz.driver.pdsuicommon.db.MySqlContext.Settings
-import xyz.driver.pdsuicommon.error.IncorrectIdException
-import xyz.driver.pdsuicommon.logging._
-
-import scala.concurrent.ExecutionContext
-import scala.util.control.NonFatal
-import scala.util.{Failure, Success, Try}
-
-object MySqlContext extends PhiLogging {
-
- final case class DbCredentials(user: String,
- password: String,
- host: String,
- port: Int,
- dbName: String,
- dbCreateFlag: Boolean,
- dbContext: String,
- connectionParams: String,
- url: String)
-
- final case class Settings(credentials: DbCredentials, connection: Config, threadPoolSize: Int)
-
- def apply(settings: Settings): MySqlContext = {
- // Prevent leaking credentials to a log
- Try(JdbcContextConfig(settings.connection).dataSource) match {
- case Success(dataSource) => new MySqlContext(dataSource, settings)
- case Failure(NonFatal(e)) =>
- logger.error(phi"Can not load dataSource, error: ${Unsafe(e.getClass.getName)}")
- throw new IllegalArgumentException("Can not load dataSource from config. Check your database and config")
- }
- }
-}
-
-class MySqlContext(dataSource: DataSource with Closeable, settings: Settings)
- extends MysqlJdbcContext[MysqlEscape](dataSource) with TransactionalContext
- with EntityExtractorDerivation[Literal] {
-
- private val tpe = Executors.newFixedThreadPool(settings.threadPoolSize)
-
- implicit val executionContext: ExecutionContext = {
- val orig = ExecutionContext.fromExecutor(tpe)
- MdcExecutionContext.from(orig)
- }
-
- override def close(): Unit = {
- super.close()
- tpe.shutdownNow()
- }
-
- /**
- * Overrode, because Quill JDBC optionDecoder pass null inside decoders.
- * If custom decoder don't have special null handler, it will failed.
- *
- * @see https://github.com/getquill/quill/issues/535
- */
- implicit override def optionDecoder[T](implicit d: Decoder[T]): Decoder[Option[T]] =
- decoder(
- sqlType = d.sqlType,
- row =>
- index => {
- try {
- val res = d(index - 1, row)
- if (row.wasNull) {
- None
- } else {
- Some(res)
- }
- } catch {
- case _: NullPointerException => None
- case _: IncorrectIdException => None
- }
- }
- )
-
- final implicit class LocalDateTimeDbOps(val left: LocalDateTime) {
-
- // scalastyle:off
- def <=(right: LocalDateTime): Quoted[Boolean] = quote(infix"$left <= $right".as[Boolean])
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/MysqlQueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/MysqlQueryBuilder.scala
deleted file mode 100644
index e2936e3..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/MysqlQueryBuilder.scala
+++ /dev/null
@@ -1,88 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.sql.ResultSet
-
-import xyz.driver.pdsuicommon.logging._
-import io.getquill.{MySQLDialect, MysqlEscape}
-
-import scala.collection.breakOut
-
-object MysqlQueryBuilder extends PhiLogging {
- import xyz.driver.pdsuicommon.db.QueryBuilder._
-
- def apply[T](tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- runner: Runner[T],
- countRunner: CountRunner): MysqlQueryBuilder[T] = {
- val parameters = MysqlQueryBuilderParameters(
- tableData = TableData(tableName, lastUpdateFieldName, nullableFields),
- links = links.map(x => x.foreignTableName -> x)(breakOut)
- )
- new MysqlQueryBuilder[T](parameters)(runner, countRunner)
- }
-
- def apply[T](tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- extractor: (ResultSet) => T)(implicit sqlContext: MySqlContext): MysqlQueryBuilder[T] = {
-
- val runner: Runner[T] = { parameters =>
- val (sql, binder) = parameters.toSql(namingStrategy = MysqlEscape)
- logger.trace(phi"Query for execute: ${Unsafe(sql)}")
- sqlContext.executeQuery[T](sql, binder, { resultSet =>
- extractor(resultSet)
- })
- }
-
- val countRunner: CountRunner = { parameters =>
- val (sql, binder) = parameters.toSql(countQuery = true, namingStrategy = MysqlEscape)
- logger.trace(phi"Query for execute: ${Unsafe(sql)}")
- sqlContext
- .executeQuery[CountResult](
- sql,
- binder, { resultSet =>
- val count = resultSet.getInt(1)
- val lastUpdate = if (parameters.tableData.lastUpdateFieldName.isDefined) {
- Option(sqlContext.localDateTimeDecoder.decoder(2, resultSet))
- } else None
-
- (count, lastUpdate)
- }
- )
- .head
- }
-
- apply[T](
- tableName = tableName,
- lastUpdateFieldName = lastUpdateFieldName,
- nullableFields = nullableFields,
- links = links,
- runner = runner,
- countRunner = countRunner
- )
- }
-}
-
-class MysqlQueryBuilder[T](parameters: MysqlQueryBuilderParameters)(implicit runner: QueryBuilder.Runner[T],
- countRunner: QueryBuilder.CountRunner)
- extends QueryBuilder[T, MySQLDialect, MysqlEscape](parameters) {
-
- def withFilter(newFilter: SearchFilterExpr): QueryBuilder[T, MySQLDialect, MysqlEscape] = {
- new MysqlQueryBuilder[T](parameters.copy(filter = newFilter))
- }
-
- def withSorting(newSorting: Sorting): QueryBuilder[T, MySQLDialect, MysqlEscape] = {
- new MysqlQueryBuilder[T](parameters.copy(sorting = newSorting))
- }
-
- def withPagination(newPagination: Pagination): QueryBuilder[T, MySQLDialect, MysqlEscape] = {
- new MysqlQueryBuilder[T](parameters.copy(pagination = Some(newPagination)))
- }
-
- def resetPagination: QueryBuilder[T, MySQLDialect, MysqlEscape] = {
- new MysqlQueryBuilder[T](parameters.copy(pagination = None))
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala b/src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala
index 7bdfd1b..0098a64 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/db/PostgresContext.scala
@@ -1,7 +1,7 @@
package xyz.driver.pdsuicommon.db
import java.io.Closeable
-import java.time._
+import java.time.{LocalDateTime, ZoneOffset}
import java.util.UUID
import java.util.concurrent.Executors
import javax.sql.DataSource
@@ -35,8 +35,7 @@ object PostgresContext extends PhiLogging {
}
class PostgresContext(val dataSource: DataSource with Closeable, settings: Settings)
- extends PostgresJdbcContext[SnakeCase](dataSource) with TransactionalContext
- with EntityExtractorDerivation[SnakeCase] {
+ extends PostgresJdbcContext[SnakeCase](dataSource) with TransactionalContext {
private val tpe = Executors.newFixedThreadPool(settings.threadPoolSize)
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/PostgresQueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/PostgresQueryBuilder.scala
deleted file mode 100644
index 0ddf811..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/PostgresQueryBuilder.scala
+++ /dev/null
@@ -1,108 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.sql.ResultSet
-
-import io.getquill.{PostgresDialect, PostgresEscape}
-import xyz.driver.pdsuicommon.db.PostgresQueryBuilder.SmartPostgresEscape
-
-import scala.collection.breakOut
-
-object PostgresQueryBuilder {
-
- import xyz.driver.pdsuicommon.db.QueryBuilder._
-
- trait SmartPostgresEscape extends PostgresEscape {
- override def column(s: String): String =
- if (s.startsWith("$")) s else super.column(s)
- override def default(s: String): String =
- s.split("\\.").map(ss => s""""$ss"""").mkString(".")
- }
-
- object SmartPostgresEscape extends SmartPostgresEscape
-
- type Escape = SmartPostgresEscape
- val Escape = SmartPostgresEscape
-
- def apply[T](tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- runner: Runner[T],
- countRunner: CountRunner): PostgresQueryBuilder[T] = {
- val parameters = PostgresQueryBuilderParameters(
- tableData = TableData(tableName, lastUpdateFieldName, nullableFields),
- links = links.map(x => x.foreignTableName -> x)(breakOut)
- )
- new PostgresQueryBuilder[T](parameters)(runner, countRunner)
- }
-
- def apply[T](tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- extractor: ResultSet => T)(implicit sqlContext: PostgresContext): PostgresQueryBuilder[T] = {
- apply(tableName, QueryBuilderParameters.AllFields, lastUpdateFieldName, nullableFields, links, extractor)
- }
-
- def apply[T](tableName: String,
- fields: Set[String],
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[TableLink],
- extractor: ResultSet => T)(implicit sqlContext: PostgresContext): PostgresQueryBuilder[T] = {
-
- val runner: Runner[T] = { parameters =>
- val (sql, binder) = parameters.toSql(countQuery = false, fields = fields, namingStrategy = SmartPostgresEscape)
- sqlContext.executeQuery[T](sql, binder, { resultSet =>
- extractor(resultSet)
- })
- }
-
- val countRunner: CountRunner = { parameters =>
- val (sql, binder) = parameters.toSql(countQuery = true, namingStrategy = SmartPostgresEscape)
- sqlContext
- .executeQuery[CountResult](
- sql,
- binder, { resultSet =>
- val count = resultSet.getInt(1)
- val lastUpdate = if (parameters.tableData.lastUpdateFieldName.isDefined) {
- Option(resultSet.getTimestamp(2)).map(sqlContext.timestampToLocalDateTime)
- } else None
-
- (count, lastUpdate)
- }
- )
- .head
- }
-
- apply[T](
- tableName = tableName,
- lastUpdateFieldName = lastUpdateFieldName,
- nullableFields = nullableFields,
- links = links,
- runner = runner,
- countRunner = countRunner
- )
- }
-}
-
-class PostgresQueryBuilder[T](parameters: PostgresQueryBuilderParameters)(implicit runner: QueryBuilder.Runner[T],
- countRunner: QueryBuilder.CountRunner)
- extends QueryBuilder[T, PostgresDialect, PostgresQueryBuilder.Escape](parameters) {
-
- def withFilter(newFilter: SearchFilterExpr): QueryBuilder[T, PostgresDialect, SmartPostgresEscape] = {
- new PostgresQueryBuilder[T](parameters.copy(filter = newFilter))
- }
-
- def withSorting(newSorting: Sorting): QueryBuilder[T, PostgresDialect, SmartPostgresEscape] = {
- new PostgresQueryBuilder[T](parameters.copy(sorting = newSorting))
- }
-
- def withPagination(newPagination: Pagination): QueryBuilder[T, PostgresDialect, SmartPostgresEscape] = {
- new PostgresQueryBuilder[T](parameters.copy(pagination = Some(newPagination)))
- }
-
- def resetPagination: QueryBuilder[T, PostgresDialect, SmartPostgresEscape] = {
- new PostgresQueryBuilder[T](parameters.copy(pagination = None))
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/QueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/QueryBuilder.scala
deleted file mode 100644
index 0bf1ed6..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/QueryBuilder.scala
+++ /dev/null
@@ -1,340 +0,0 @@
-package xyz.driver.pdsuicommon.db
-
-import java.sql.PreparedStatement
-import java.time.LocalDateTime
-
-import io.getquill.NamingStrategy
-import io.getquill.context.sql.idiom.SqlIdiom
-import xyz.driver.pdsuicommon.db.Sorting.{Dimension, Sequential}
-import xyz.driver.pdsuicommon.db.SortingOrder.{Ascending, Descending}
-
-import scala.collection.mutable.ListBuffer
-
-object QueryBuilder {
-
- type Runner[T] = QueryBuilderParameters => Seq[T]
-
- type CountResult = (Int, Option[LocalDateTime])
-
- type CountRunner = QueryBuilderParameters => CountResult
-
- /**
- * Binder for PreparedStatement
- */
- type Binder = PreparedStatement => PreparedStatement
-
- final case class TableData(tableName: String,
- lastUpdateFieldName: Option[String] = None,
- nullableFields: Set[String] = Set.empty)
-
- val AllFields = Set("*")
-
-}
-
-final case class TableLink(keyColumnName: String, foreignTableName: String, foreignKeyColumnName: String)
-
-object QueryBuilderParameters {
- val AllFields = Set("*")
-}
-
-sealed trait QueryBuilderParameters {
-
- def tableData: QueryBuilder.TableData
- def links: Map[String, TableLink]
- def filter: SearchFilterExpr
- def sorting: Sorting
- def pagination: Option[Pagination]
-
- def findLink(tableName: String): TableLink = links.get(tableName) match {
- case None => throw new IllegalArgumentException(s"Cannot find a link for `$tableName`")
- case Some(link) => link
- }
-
- def toSql(countQuery: Boolean = false, namingStrategy: NamingStrategy): (String, QueryBuilder.Binder) = {
- toSql(countQuery, QueryBuilderParameters.AllFields, namingStrategy)
- }
-
- def toSql(countQuery: Boolean, fields: Set[String], namingStrategy: NamingStrategy): (String, QueryBuilder.Binder) = {
- val escapedTableName = namingStrategy.table(tableData.tableName)
- val fieldsSql: String = if (countQuery) {
- val suffix: String = tableData.lastUpdateFieldName match {
- case Some(lastUpdateField) => s", max($escapedTableName.${namingStrategy.column(lastUpdateField)})"
- case None => ""
- }
- "count(*)" + suffix
- } else {
- if (fields == QueryBuilderParameters.AllFields) {
- s"$escapedTableName.*"
- } else {
- fields
- .map { field =>
- s"$escapedTableName.${namingStrategy.column(field)}"
- }
- .mkString(", ")
- }
- }
- val (where, bindings) = filterToSql(escapedTableName, filter, namingStrategy)
- val orderBy = sortingToSql(escapedTableName, sorting, namingStrategy)
-
- val limitSql = limitToSql()
-
- val sql = new StringBuilder()
- sql.append("select ")
- sql.append(fieldsSql)
- sql.append("\nfrom ")
- sql.append(escapedTableName)
-
- val filtersTableLinks: Seq[TableLink] = {
- import SearchFilterExpr._
- def aux(expr: SearchFilterExpr): Seq[TableLink] = expr match {
- case Atom.TableName(tableName) => List(findLink(tableName))
- case Intersection(xs) => xs.flatMap(aux)
- case Union(xs) => xs.flatMap(aux)
- case _ => Nil
- }
- aux(filter)
- }
-
- val sortingTableLinks: Seq[TableLink] = Sorting.collect(sorting) {
- case Dimension(Some(foreignTableName), _, _) => findLink(foreignTableName)
- }
-
- // Combine links from sorting and filter without duplicates
- val foreignTableLinks = (filtersTableLinks ++ sortingTableLinks).distinct
-
- foreignTableLinks.foreach {
- case TableLink(keyColumnName, foreignTableName, foreignKeyColumnName) =>
- val escapedForeignTableName = namingStrategy.table(foreignTableName)
-
- sql.append("\ninner join ")
- sql.append(escapedForeignTableName)
- sql.append(" on ")
-
- sql.append(escapedTableName)
- sql.append('.')
- sql.append(namingStrategy.column(keyColumnName))
-
- sql.append(" = ")
-
- sql.append(escapedForeignTableName)
- sql.append('.')
- sql.append(namingStrategy.column(foreignKeyColumnName))
- }
-
- if (where.nonEmpty) {
- sql.append("\nwhere ")
- sql.append(where)
- }
-
- if (orderBy.nonEmpty && !countQuery) {
- sql.append("\norder by ")
- sql.append(orderBy)
- }
-
- if (limitSql.nonEmpty && !countQuery) {
- sql.append("\n")
- sql.append(limitSql)
- }
-
- (sql.toString, binder(bindings))
- }
-
- /**
- * Converts filter expression to SQL expression.
- *
- * @return Returns SQL string and list of values for binding in prepared statement.
- */
- protected def filterToSql(escapedTableName: String,
- filter: SearchFilterExpr,
- namingStrategy: NamingStrategy): (String, List[AnyRef]) = {
- import SearchFilterBinaryOperation._
- import SearchFilterExpr._
-
- def isNull(string: AnyRef) = Option(string).isEmpty || string.toString.toLowerCase == "null"
-
- def placeholder(field: String) = "?"
-
- def escapeDimension(dimension: SearchFilterExpr.Dimension) = {
- val tableName = dimension.tableName.fold(escapedTableName)(namingStrategy.table)
- s"$tableName.${namingStrategy.column(dimension.name)}"
- }
-
- def filterToSqlMultiple(operands: Seq[SearchFilterExpr]) = operands.collect {
- case x if !SearchFilterExpr.isEmpty(x) => filterToSql(escapedTableName, x, namingStrategy)
- }
-
- filter match {
- case x if isEmpty(x) =>
- ("", List.empty)
-
- case AllowAll =>
- ("1", List.empty)
-
- case DenyAll =>
- ("0", List.empty)
-
- case Atom.Binary(dimension, Eq, value) if isNull(value) =>
- (s"${escapeDimension(dimension)} is NULL", List.empty)
-
- case Atom.Binary(dimension, NotEq, value) if isNull(value) =>
- (s"${escapeDimension(dimension)} is not NULL", List.empty)
-
- case Atom.Binary(dimension, NotEq, value) if tableData.nullableFields.contains(dimension.name) =>
- // In MySQL NULL <> Any === NULL
- // So, to handle NotEq for nullable fields we need to use more complex SQL expression.
- // http://dev.mysql.com/doc/refman/5.7/en/working-with-null.html
- val escapedColumn = escapeDimension(dimension)
- val sql = s"($escapedColumn is null or $escapedColumn != ${placeholder(dimension.name)})"
- (sql, List(value))
-
- case Atom.Binary(dimension, op, value) =>
- val operator = op match {
- case Eq => "="
- case NotEq => "!="
- case Like => "like"
- case Gt => ">"
- case GtEq => ">="
- case Lt => "<"
- case LtEq => "<="
- }
- (s"${escapeDimension(dimension)} $operator ${placeholder(dimension.name)}", List(value))
-
- case Atom.NAry(dimension, op, values) =>
- val sqlOp = op match {
- case SearchFilterNAryOperation.In => "in"
- case SearchFilterNAryOperation.NotIn => "not in"
- }
-
- val bindings = ListBuffer[AnyRef]()
- val sqlPlaceholder = placeholder(dimension.name)
- val formattedValues = if (values.nonEmpty) {
- values
- .map { value =>
- bindings += value
- sqlPlaceholder
- }
- .mkString(", ")
- } else "NULL"
- (s"${escapeDimension(dimension)} $sqlOp ($formattedValues)", bindings.toList)
-
- case Intersection(operands) =>
- val (sql, bindings) = filterToSqlMultiple(operands).unzip
- (sql.mkString("(", " and ", ")"), bindings.flatten.toList)
-
- case Union(operands) =>
- val (sql, bindings) = filterToSqlMultiple(operands).unzip
- (sql.mkString("(", " or ", ")"), bindings.flatten.toList)
- }
- }
-
- protected def limitToSql(): String
-
- /**
- * @param escapedMainTableName Should be escaped
- */
- protected def sortingToSql(escapedMainTableName: String, sorting: Sorting, namingStrategy: NamingStrategy): String = {
- sorting match {
- case Dimension(optSortingTableName, field, order) =>
- val sortingTableName = optSortingTableName.map(namingStrategy.table).getOrElse(escapedMainTableName)
- val fullName = s"$sortingTableName.${namingStrategy.column(field)}"
-
- s"$fullName ${orderToSql(order)}"
-
- case Sequential(xs) =>
- xs.map(sortingToSql(escapedMainTableName, _, namingStrategy)).mkString(", ")
- }
- }
-
- protected def orderToSql(x: SortingOrder): String = x match {
- case Ascending => "asc"
- case Descending => "desc"
- }
-
- protected def binder(bindings: List[AnyRef])(bind: PreparedStatement): PreparedStatement = {
- bindings.zipWithIndex.foreach {
- case (binding, index) =>
- bind.setObject(index + 1, binding)
- }
-
- bind
- }
-
-}
-
-final case class PostgresQueryBuilderParameters(tableData: QueryBuilder.TableData,
- links: Map[String, TableLink] = Map.empty,
- filter: SearchFilterExpr = SearchFilterExpr.Empty,
- sorting: Sorting = Sorting.Empty,
- pagination: Option[Pagination] = None)
- extends QueryBuilderParameters {
-
- def limitToSql(): String = {
- pagination.map { pagination =>
- val startFrom = (pagination.pageNumber - 1) * pagination.pageSize
- s"limit ${pagination.pageSize} OFFSET $startFrom"
- } getOrElse ""
- }
-
-}
-
-/**
- * @param links Links to another tables grouped by foreignTableName
- */
-final case class MysqlQueryBuilderParameters(tableData: QueryBuilder.TableData,
- links: Map[String, TableLink] = Map.empty,
- filter: SearchFilterExpr = SearchFilterExpr.Empty,
- sorting: Sorting = Sorting.Empty,
- pagination: Option[Pagination] = None)
- extends QueryBuilderParameters {
-
- def limitToSql(): String =
- pagination
- .map { pagination =>
- val startFrom = (pagination.pageNumber - 1) * pagination.pageSize
- s"limit $startFrom, ${pagination.pageSize}"
- }
- .getOrElse("")
-
-}
-
-abstract class QueryBuilder[T, D <: SqlIdiom, N <: NamingStrategy](val parameters: QueryBuilderParameters)(
- implicit runner: QueryBuilder.Runner[T],
- countRunner: QueryBuilder.CountRunner) {
-
- def run: Seq[T] = runner(parameters)
-
- def runCount: QueryBuilder.CountResult = countRunner(parameters)
-
- /**
- * Runs the query and returns total found rows without considering of pagination.
- */
- def runWithCount: (Seq[T], Int, Option[LocalDateTime]) = {
- val (total, lastUpdate) = runCount
- (run, total, lastUpdate)
- }
-
- def withFilter(newFilter: SearchFilterExpr): QueryBuilder[T, D, N]
-
- def withFilter(filter: Option[SearchFilterExpr]): QueryBuilder[T, D, N] = {
- filter.fold(this)(withFilter)
- }
-
- def resetFilter: QueryBuilder[T, D, N] = withFilter(SearchFilterExpr.Empty)
-
- def withSorting(newSorting: Sorting): QueryBuilder[T, D, N]
-
- def withSorting(sorting: Option[Sorting]): QueryBuilder[T, D, N] = {
- sorting.fold(this)(withSorting)
- }
-
- def resetSorting: QueryBuilder[T, D, N] = withSorting(Sorting.Empty)
-
- def withPagination(newPagination: Pagination): QueryBuilder[T, D, N]
-
- def withPagination(pagination: Option[Pagination]): QueryBuilder[T, D, N] = {
- pagination.fold(this)(withPagination)
- }
-
- def resetPagination: QueryBuilder[T, D, N]
-
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala
index 3011f6a..9962edf 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala
@@ -83,7 +83,7 @@ sealed trait SlickQueryBuilderParameters {
}
def toSql(countQuery: Boolean = false)(implicit profile: JdbcProfile): SQLActionBuilder = {
- toSql(countQuery, QueryBuilderParameters.AllFields)
+ toSql(countQuery, SlickQueryBuilderParameters.AllFields)
}
def toSql(countQuery: Boolean, fields: Set[String])(implicit profile: JdbcProfile): SQLActionBuilder = {
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/BridgeUploadQueueRepository.scala b/src/main/scala/xyz/driver/pdsuicommon/db/repositories/BridgeUploadQueueRepository.scala
deleted file mode 100644
index 4c25afa..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/BridgeUploadQueueRepository.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-package xyz.driver.pdsuicommon.db.repositories
-
-import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueue
-import xyz.driver.pdsuicommon.db.MysqlQueryBuilder
-
-trait BridgeUploadQueueRepository extends Repository {
-
- type EntityT = BridgeUploadQueue.Item
-
- def add(draft: EntityT): EntityT
-
- def getById(kind: String, tag: String): Option[EntityT]
-
- def getOne(kind: String): Option[BridgeUploadQueue.Item]
-
- def update(entity: EntityT): EntityT
-
- def delete(kind: String, tag: String): Unit
-
- def buildQuery: MysqlQueryBuilder[EntityT]
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala b/src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala
index d671e80..e62238e 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/db/repositories/Repository.scala
@@ -1,4 +1,3 @@
package xyz.driver.pdsuicommon.db.repositories
-// For further usage and migration to Postgres and slick
trait Repository extends RepositoryLogging
diff --git a/src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala b/src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala
index 5574c01..748e76a 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala
@@ -1,7 +1,6 @@
package xyz.driver.pdsuicommon.error
-import play.api.libs.functional.syntax._
-import play.api.libs.json.{Format, Reads, Writes}
+import xyz.driver.core.json.EnumJsonFormat
@SuppressWarnings(Array("org.wartremover.warts.Enumeration"))
object ErrorCode extends Enumeration {
@@ -9,9 +8,13 @@ object ErrorCode extends Enumeration {
type ErrorCode = Value
val Unspecified = Value(1)
- private val fromJsonReads: Reads[ErrorCode] = Reads.of[Int].map(ErrorCode.apply)
- private val toJsonWrites: Writes[ErrorCode] = Writes.of[Int].contramap(_.id)
-
- implicit val jsonFormat: Format[ErrorCode] = Format(fromJsonReads, toJsonWrites)
+ implicit val jsonFormat = new EnumJsonFormat[ErrorCode](
+ "200" -> ErrorCode.Value(200),
+ "400" -> ErrorCode.Value(400),
+ "401" -> ErrorCode.Value(401),
+ "403" -> ErrorCode.Value(403),
+ "404" -> ErrorCode.Value(404),
+ "500" -> ErrorCode.Value(500)
+ )
}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala b/src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala
index 3761cc5..ccb84c2 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala
@@ -1,18 +1,13 @@
package xyz.driver.pdsuicommon.error
-import xyz.driver.pdsuicommon.json.Serialization.seqJsonFormat
-import ErrorCode.{ErrorCode, Unspecified}
+import spray.json._
import ErrorsResponse.ResponseError
-import xyz.driver.pdsuicommon.auth.{AnonymousRequestContext, RequestId}
-import xyz.driver.pdsuicommon.utils.Utils
-import play.api.libs.functional.syntax._
-import play.api.libs.json._
-import play.api.mvc.Results
-import xyz.driver.pdsuicommon.validation.JsonValidationErrors
+import xyz.driver.pdsuicommon.auth.RequestId
final case class ErrorsResponse(errors: Seq[ResponseError], requestId: RequestId)
object ErrorsResponse {
+ import DefaultJsonProtocol._
/**
* @param data Any data that can be associated with particular error.Ex.: error field name
@@ -21,63 +16,38 @@ object ErrorsResponse {
*
* @see https://driverinc.atlassian.net/wiki/display/RA/REST+API+Specification#RESTAPISpecification-HTTPStatuscodes
*/
- final case class ResponseError(data: Option[String], message: String, code: ErrorCode)
+ final case class ResponseError(data: Option[String], message: String, code: Int)
object ResponseError {
- implicit val responseErrorJsonFormat: Format[ResponseError] = (
- (JsPath \ "data").formatNullable[String] and
- (JsPath \ "message").format[String] and
- (JsPath \ "code").format[ErrorCode]
- )(ResponseError.apply, unlift(ResponseError.unapply))
+ implicit val responseErrorJsonFormat: RootJsonFormat[ResponseError] = jsonFormat3(ResponseError.apply)
}
- implicit val errorsResponseJsonFormat: Format[ErrorsResponse] = (
- (JsPath \ "errors").format[Seq[ResponseError]] and
- (JsPath \ "requestId").format[String]
- )((errs, req) => ErrorsResponse.apply(errs, RequestId(req)), res => (res.errors, res.requestId.value))
+ implicit val errorsResponseJsonFormat: RootJsonFormat[ErrorsResponse] = new RootJsonFormat[ErrorsResponse] {
+ override def write(obj: ErrorsResponse): JsValue = {
+ JsObject(
+ "errors" -> obj.errors.map(_.toJson).toJson,
+ "requestId" -> obj.requestId.value.toJson
+ )
+ }
- // deprecated, will be removed in REP-436
- def fromString(message: String, httpStatus: Results#Status)(
- implicit context: AnonymousRequestContext): ErrorsResponse = {
- new ErrorsResponse(
- errors = Seq(
- ResponseError(
- data = None,
- message = message,
- code = Unspecified
- )),
- requestId = context.requestId
- )
- }
+ override def read(json: JsValue) = json match {
+ case JsObject(fields) =>
+ val errors = fields
+ .get("errors")
+ .map(_.convertTo[Seq[ResponseError]])
+ .getOrElse(deserializationError(s"ErrorsResponse json object does not contain `errors` field: $json"))
- // scalastyle:off null
- def fromExceptionMessage(e: Throwable, httpStatus: Results#Status = Results.InternalServerError)(
- implicit context: AnonymousRequestContext): ErrorsResponse = {
- val message = if (e.getMessage == null || e.getMessage.isEmpty) {
- Utils.getClassSimpleName(e.getClass)
- } else {
- e.getMessage
- }
+ val requestId = fields
+ .get("requestId")
+ .map(id => RequestId(id.convertTo[String]))
+ .getOrElse(deserializationError(s"ErrorsResponse json object does not contain `requestId` field: $json"))
- fromString(message, httpStatus)
- }
- // scalastyle:on null
+ ErrorsResponse(errors, requestId)
- // deprecated, will be removed in REP-436
- def fromJsonValidationErrors(validationErrors: JsonValidationErrors)(
- implicit context: AnonymousRequestContext): ErrorsResponse = {
- val errors = validationErrors.map {
- case (path, xs) =>
- ResponseError(
- data = Some(path.toString()),
- message = xs.map(_.message).mkString("\n"),
- code = Unspecified
- )
+ case _ => deserializationError(s"Expected json as ErrorsResponse, but got $json")
}
-
- new ErrorsResponse(errors, context.requestId)
}
}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala b/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala
index d7d3df5..4f5cd8a 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala
@@ -13,9 +13,8 @@ import xyz.driver.pdsuicommon.error.ErrorsResponse.ResponseError
import xyz.driver.pdsuicommon.parsers._
import xyz.driver.pdsuicommon.db.{Pagination, SearchFilterExpr, Sorting}
import xyz.driver.pdsuicommon.domain._
-import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import xyz.driver.core.rest.auth.AuthProvider
-
import scala.util.control._
import scala.util._
@@ -72,7 +71,7 @@ trait Directives {
def domainExceptionHandler(req: RequestId): ExceptionHandler = {
def errorResponse(ex: Throwable) =
- ErrorsResponse(Seq(ResponseError(None, ex.getMessage, ErrorCode.Unspecified)), req)
+ ErrorsResponse(Seq(ResponseError(None, ex.getMessage, 1)), req)
ExceptionHandler {
case ex: AuthenticationException => complete(StatusCodes.Unauthorized -> errorResponse(ex))
case ex: AuthorizationException => complete(StatusCodes.Forbidden -> errorResponse(ex))
@@ -84,9 +83,9 @@ trait Directives {
def domainRejectionHandler(req: RequestId): RejectionHandler = {
def wrapContent(message: String) = {
- import play.api.libs.json._
- val err = ErrorsResponse(Seq(ResponseError(None, message, ErrorCode.Unspecified)), req)
- val text = Json.stringify(implicitly[Writes[ErrorsResponse]].writes(err))
+ import ErrorsResponse._
+ val err: ErrorsResponse = ErrorsResponse(Seq(ResponseError(None, message, 1)), req)
+ val text = errorsResponseJsonFormat.write(err).toString()
HttpEntity(ContentTypes.`application/json`, text)
}
DriverApp.rejectionHandler.mapRejectionResponse {
diff --git a/src/main/scala/xyz/driver/pdsuicommon/json/JsResultOps.scala b/src/main/scala/xyz/driver/pdsuicommon/json/JsResultOps.scala
deleted file mode 100644
index 4ff4034..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/json/JsResultOps.scala
+++ /dev/null
@@ -1,15 +0,0 @@
-package xyz.driver.pdsuicommon.json
-
-import play.api.libs.json.JsResult
-
-import scala.util.{Failure, Success, Try}
-
-final class JsResultOps[T](val self: JsResult[T]) extends AnyVal {
-
- def toTry: Try[T] = {
- self.fold[Try[T]](
- errors => Failure(new JsonValidationException(errors)),
- Success(_)
- )
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/json/JsonValidationException.scala b/src/main/scala/xyz/driver/pdsuicommon/json/JsonValidationException.scala
deleted file mode 100644
index 21750b4..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/json/JsonValidationException.scala
+++ /dev/null
@@ -1,5 +0,0 @@
-package xyz.driver.pdsuicommon.json
-
-import xyz.driver.pdsuicommon.validation.JsonValidationErrors
-
-class JsonValidationException(val errors: JsonValidationErrors) extends Exception
diff --git a/src/main/scala/xyz/driver/pdsuicommon/json/Serialization.scala b/src/main/scala/xyz/driver/pdsuicommon/json/Serialization.scala
deleted file mode 100644
index 8231ddb..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/json/Serialization.scala
+++ /dev/null
@@ -1,46 +0,0 @@
-package xyz.driver.pdsuicommon.json
-
-import java.net.URI
-
-import play.api.libs.functional.syntax._
-import play.api.libs.json._
-import xyz.driver.pdsuicommon.domain._
-import xyz.driver.pdsuidomain.entities.CaseId
-
-object Serialization {
-
- // @TODO Test and check all items in an array
- private def seqJsonReads[T](implicit argFormat: Reads[T]): Reads[Seq[T]] = Reads {
- case JsArray(xs) =>
- JsSuccess(xs.map { x =>
- argFormat.reads(x).get
- })
- case x => JsError(s"Expected JsArray, but got $x")
- }
-
- private def seqJsonWrites[T](implicit argFormat: Writes[T]): Writes[Seq[T]] = Writes { xs =>
- JsArray(xs.map(argFormat.writes))
- }
-
- implicit def seqJsonFormat[T](implicit f: Format[T]): Format[Seq[T]] = Format(seqJsonReads[T], seqJsonWrites[T])
-
- private val uriJsonReads: Reads[URI] = Reads.StringReads.map(URI.create)
- private val uriJsonWrites: Writes[URI] = Writes(uri => JsString(uri.toString))
- implicit val uriJsonFormat: Format[URI] = Format(uriJsonReads, uriJsonWrites)
-
- private def uuidIdJsonReads[T]: Reads[UuidId[T]] = Reads.uuidReads.map(x => UuidId[T](x))
- private def uuidIdJsonWrites[T]: Writes[UuidId[T]] = Writes.UuidWrites.contramap(_.id)
- implicit def uuidIdJsonFormat[T]: Format[UuidId[T]] = Format(uuidIdJsonReads, uuidIdJsonWrites)
-
- private def longIdJsonReads[T]: Reads[LongId[T]] = Reads.LongReads.map(x => LongId[T](x))
- private def longIdJsonWrites[T]: Writes[LongId[T]] = Writes.LongWrites.contramap(_.id)
- implicit def longIdJsonFormat[T]: Format[LongId[T]] = Format(longIdJsonReads, longIdJsonWrites)
-
- private val emailJsonReads: Reads[Email] = Reads.email.map(Email.apply)
- private val emailJsonWrites: Writes[Email] = Writes(email => JsString(email.value))
- implicit val emailJsonFormat: Format[Email] = Format(emailJsonReads, emailJsonWrites)
-
- private val caseIdJsonReads: Reads[CaseId] = Reads.StringReads.map(CaseId(_))
- private val caseIdJsonWrites: Writes[CaseId] = Writes(caseId => JsString(caseId.id))
- implicit val caseIdJsonFormat: Format[CaseId] = Format(caseIdJsonReads, caseIdJsonWrites)
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/serialization/PlayJsonSupport.scala b/src/main/scala/xyz/driver/pdsuicommon/serialization/PlayJsonSupport.scala
deleted file mode 100644
index 5158dab..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/serialization/PlayJsonSupport.scala
+++ /dev/null
@@ -1,34 +0,0 @@
-package xyz.driver.pdsuicommon.serialization
-
-import akka.http.scaladsl.server.{RejectionError, ValidationRejection}
-import akka.http.scaladsl.unmarshalling.Unmarshaller
-import play.api.libs.json.{Reads, Writes}
-import play.api.libs.json.Json
-import akka.http.scaladsl.marshalling.ToEntityMarshaller
-import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller
-import akka.http.scaladsl.model.MediaTypes.`application/json`
-
-trait PlayJsonSupport {
- import akka.http.scaladsl.marshalling.Marshaller
-
- implicit def playJsonUnmarshaller[A: Reads]: FromEntityUnmarshaller[A] = {
- val reads = implicitly[Reads[A]]
- Unmarshaller.stringUnmarshaller
- .forContentTypes(`application/json`)
- .map(Json.parse)
- .map(reads.reads)
- .map(_.recoverTotal { error =>
- throw RejectionError(ValidationRejection(s"Error reading JSON response as ${reads}."))
- })
- }
-
- implicit def playJsonMarshaller[A: Writes]: ToEntityMarshaller[A] = {
- Marshaller
- .stringMarshaller(`application/json`)
- .compose(Json.prettyPrint)
- .compose(implicitly[Writes[A]].writes)
- }
-
-}
-
-object PlayJsonSupport extends PlayJsonSupport
diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/CustomSwaggerJsonFormats.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/CustomSwaggerJsonFormats.scala
index 34c7495..a79071e 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/utils/CustomSwaggerJsonFormats.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/utils/CustomSwaggerJsonFormats.scala
@@ -6,16 +6,16 @@ import io.swagger.models.properties.Property
import spray.json.JsValue
import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId}
import xyz.driver.pdsuidomain.entities._
-import xyz.driver.pdsuidomain.formats.json.sprayformats.listresponse._
+import xyz.driver.pdsuidomain.formats.json.listresponse._
import xyz.driver.core.swagger.CustomSwaggerJsonConverter._
import xyz.driver.entities.patient.CancerType
import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueue
import xyz.driver.pdsuidomain.entities.export.patient.ExportPatientWithLabels
import xyz.driver.pdsuidomain.entities.export.trial.ExportTrialWithLabels
import xyz.driver.pdsuidomain.fakes.entities.common
-import xyz.driver.pdsuidomain.formats.json.sprayformats.bridgeuploadqueue._
-import xyz.driver.pdsuidomain.formats.json.sprayformats.record._
-import xyz.driver.pdsuidomain.formats.json.sprayformats.document._
+import xyz.driver.pdsuidomain.formats.json.bridgeuploadqueue._
+import xyz.driver.pdsuidomain.formats.json.record._
+import xyz.driver.pdsuidomain.formats.json.document._
import xyz.driver.pdsuidomain.services.CriterionService.RichCriterion
import xyz.driver.pdsuidomain.services.ExtractedDataService.RichExtractedData
import xyz.driver.pdsuidomain.services.PatientCriterionService.{DraftPatientCriterion, RichPatientCriterion}
@@ -83,17 +83,17 @@ object CustomSwaggerJsonFormats {
object trialcuration {
import xyz.driver.pdsuidomain.fakes.entities.trialcuration._
import xyz.driver.pdsuidomain.fakes.entities.export
- import xyz.driver.pdsuidomain.formats.json.sprayformats.export._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.arm._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.slotarm._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.eligibilityarm._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.criterion._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.intervention._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.hypothesis._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.studydesign._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.trial._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.trialhistory._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.trialissue._
+ import xyz.driver.pdsuidomain.formats.json.export._
+ import xyz.driver.pdsuidomain.formats.json.arm._
+ import xyz.driver.pdsuidomain.formats.json.slotarm._
+ import xyz.driver.pdsuidomain.formats.json.eligibilityarm._
+ import xyz.driver.pdsuidomain.formats.json.criterion._
+ import xyz.driver.pdsuidomain.formats.json.intervention._
+ import xyz.driver.pdsuidomain.formats.json.hypothesis._
+ import xyz.driver.pdsuidomain.formats.json.studydesign._
+ import xyz.driver.pdsuidomain.formats.json.trial._
+ import xyz.driver.pdsuidomain.formats.json.trialhistory._
+ import xyz.driver.pdsuidomain.formats.json.trialissue._
val customTrialCurationProperties = immutable.Map[Class[_], Property](
classOf[Trial.Status] -> stringProperty(),
@@ -102,7 +102,7 @@ object CustomSwaggerJsonFormats {
) ++ customCommonProperties
val customTrialCurationObjectsExamples = immutable.Map[Class[_], JsValue](
- classOf[Trial] -> trialWriter.write(nextTrial()),
+ classOf[Trial] -> trialFormat.write(nextTrial()),
classOf[Arm] -> armFormat.write(nextArm()),
classOf[TrialHistory] -> trialHistoryFormat.write(nextTrialHistory()),
classOf[TrialIssue] -> trialIssueWriter.write(nextTrialIssue()),
@@ -134,12 +134,12 @@ object CustomSwaggerJsonFormats {
object recordprocessing {
import xyz.driver.pdsuidomain.fakes.entities.recordprocessing._
import xyz.driver.pdsuidomain.fakes.entities.export
- import xyz.driver.pdsuidomain.formats.json.sprayformats.export._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.documentissue._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.documenthistory._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.recordissue._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.recordhistory._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.extracteddata._
+ import xyz.driver.pdsuidomain.formats.json.export._
+ import xyz.driver.pdsuidomain.formats.json.documentissue._
+ import xyz.driver.pdsuidomain.formats.json.documenthistory._
+ import xyz.driver.pdsuidomain.formats.json.recordissue._
+ import xyz.driver.pdsuidomain.formats.json.recordhistory._
+ import xyz.driver.pdsuidomain.formats.json.extracteddata._
val customRecordProcessingProperties = immutable.Map[Class[_], Property](
classOf[MedicalRecord.Status] -> stringProperty(),
@@ -176,14 +176,14 @@ object CustomSwaggerJsonFormats {
object treatmentmatching {
import xyz.driver.pdsuidomain.fakes.entities.treatmentmatching._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.patient._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.patientcriterion._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.patientdefiningcriteria._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.patienteligibletrial._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.patientlabel._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.patienthypothesis._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.patienthistory._
- import xyz.driver.pdsuidomain.formats.json.sprayformats.patientissue._
+ import xyz.driver.pdsuidomain.formats.json.patient._
+ import xyz.driver.pdsuidomain.formats.json.patientcriterion._
+ import xyz.driver.pdsuidomain.formats.json.patientdefiningcriteria._
+ import xyz.driver.pdsuidomain.formats.json.patienteligibletrial._
+ import xyz.driver.pdsuidomain.formats.json.patientlabel._
+ import xyz.driver.pdsuidomain.formats.json.patienthypothesis._
+ import xyz.driver.pdsuidomain.formats.json.patienthistory._
+ import xyz.driver.pdsuidomain.formats.json.patientissue._
val customTreatmentMatchingProperties = immutable.Map[Class[_], Property](
classOf[Patient.Status] -> stringProperty(),
@@ -192,7 +192,7 @@ object CustomSwaggerJsonFormats {
) ++ customCommonProperties
val customTreatmentMatchingObjectsExamples = immutable.Map[Class[_], JsValue](
- classOf[Patient] -> patientWriter.write(nextPatient()),
+ classOf[Patient] -> patientFormat.write(nextPatient()),
classOf[RichPatientLabel] -> richPatientLabelWriter.write(nextRichPatientLabel()),
classOf[PatientLabel] -> patientLabelDefiningCriteriaWriter.write(nextPatientLabel()),
classOf[RichPatientCriterion] -> patientCriterionWriter.write(nextRichPatientCriterion()),
diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/Implicits.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/Implicits.scala
index 9411beb..0b18093 100644
--- a/src/main/scala/xyz/driver/pdsuicommon/utils/Implicits.scala
+++ b/src/main/scala/xyz/driver/pdsuicommon/utils/Implicits.scala
@@ -1,8 +1,5 @@
package xyz.driver.pdsuicommon.utils
-import play.api.libs.json.JsResult
-import xyz.driver.pdsuicommon.json.JsResultOps
-
import scala.collection.generic.CanBuildFrom
object Implicits {
@@ -23,7 +20,6 @@ object Implicits {
implicit def toMapOps[K, V](x: Map[K, V]): MapOps[K, V] = new MapOps(x)
- implicit def toCharOps(self: Char): CharOps = new CharOps(self)
- implicit def toStringOps(self: String): StringOps = new StringOps(self)
- implicit def toJsResultOps[T](self: JsResult[T]): JsResultOps[T] = new JsResultOps(self)
+ implicit def toCharOps(self: Char): CharOps = new CharOps(self)
+ implicit def toStringOps(self: String): StringOps = new StringOps(self)
}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/WriteableImplicits.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/WriteableImplicits.scala
deleted file mode 100644
index 6c04dfa..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/utils/WriteableImplicits.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-package xyz.driver.pdsuicommon.utils
-
-import play.api.http.{ContentTypes, Writeable}
-import play.api.libs.json.{Json, Writes}
-
-// @TODO this should be an object with a method, that gets HTTP-headers and returns suitable Writeable
-trait WriteableImplicits {
-
- // Write JSON by default at now
- implicit def defaultWriteable[T](implicit inner: Writes[T]) = Writeable[T](
- { x: T =>
- Writeable.writeableOf_JsValue.transform(Json.toJson(x))
- },
- Option(ContentTypes.JSON)
- )
-
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/utils/WritesUtils.scala b/src/main/scala/xyz/driver/pdsuicommon/utils/WritesUtils.scala
deleted file mode 100644
index 3476a1e..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/utils/WritesUtils.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-package xyz.driver.pdsuicommon.utils
-
-import play.api.libs.json._
-
-object WritesUtils {
-
- def filterKeys[T](p: String => Boolean)(implicit w: Writes[T]): Writes[T] = {
- filter {
- case (key, _) => p(key)
- }
- }
-
- def filter[T](p: (String, JsValue) => Boolean)(implicit w: Writes[T]): Writes[T] = {
- w.transform { input: JsValue =>
- input match {
- case JsObject(map) => JsObject(map.filter(Function.tupled(p)))
- case x => x
- }
- }
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/validation/AdditionalConstraints.scala b/src/main/scala/xyz/driver/pdsuicommon/validation/AdditionalConstraints.scala
deleted file mode 100644
index cb1082f..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/validation/AdditionalConstraints.scala
+++ /dev/null
@@ -1,33 +0,0 @@
-package xyz.driver.pdsuicommon.validation
-
-import org.davidbild.tristate.Tristate
-import play.api.data.validation._
-
-object AdditionalConstraints {
-
- val nonNegativePrintedNumber: Constraint[String] = {
- Constraints.pattern("^\\d+$".r, "printedInt.nonNegative", "must be a non-negative number")
- }
-
- val positivePrintedNumber: Constraint[String] = {
- Constraints.pattern("^[1-9]\\d*$".r, "printedInt.positive", "must be a positive number")
- }
-
- val optionNonEmptyConstraint: Constraint[Option[Any]] = {
- Constraint("option.nonEmpty") {
- case Some(x) => Valid
- case None => Invalid("is empty")
- }
- }
-
- val tristateSpecifiedConstraint: Constraint[Tristate[Any]] = {
- Constraint("tristate.specified") {
- case Tristate.Unspecified => Invalid("unspecified")
- case _ => Valid
- }
- }
-
- val uuid: Constraint[String] = {
- Constraints.pattern("""[\da-z]{8}-[\da-z]{4}-[\da-z]{4}-[\da-z]{4}-[\da-z]{12}""".r, "uuid", "invalid uuid")
- }
-}
diff --git a/src/main/scala/xyz/driver/pdsuicommon/validation/package.scala b/src/main/scala/xyz/driver/pdsuicommon/validation/package.scala
deleted file mode 100644
index 9a31a93..0000000
--- a/src/main/scala/xyz/driver/pdsuicommon/validation/package.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-package xyz.driver.pdsuicommon
-
-import play.api.data.validation.{ValidationError => PlayValidationError}
-import play.api.libs.json.JsPath
-
-package object validation {
- type JsonValidationErrors = Seq[(JsPath, Seq[PlayValidationError])]
-}