aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvlad <vlad@driver.xyz>2018-01-25 14:13:07 -0800
committervlad <vlad@driver.xyz>2018-01-25 14:13:07 -0800
commit3dd31307563576031e11607f04547d3a6fc84344 (patch)
tree11a948bb4794a404404c12dc61000829735ed6bd
parenta0877d81ca2844d75dc361b5ce7c99afacd6e25f (diff)
downloadrest-query-3dd31307563576031e11607f04547d3a6fc84344.tar.gz
rest-query-3dd31307563576031e11607f04547d3a6fc84344.tar.bz2
rest-query-3dd31307563576031e11607f04547d3a6fc84344.zip
Extracting query library
-rw-r--r--README.md20
-rw-r--r--build.sbt19
-rw-r--r--project/build.properties2
-rw-r--r--src/main/scala/xyz/driver/restquery/db/SlickPostgresQueryBuilder.scala78
-rw-r--r--src/main/scala/xyz/driver/restquery/db/SlickQueryBuilder.scala299
-rw-r--r--src/main/scala/xyz/driver/restquery/db/SlickQueryBuilderParameters.scala19
-rw-r--r--src/main/scala/xyz/driver/restquery/query/Pagination.scala2
-rw-r--r--src/main/scala/xyz/driver/restquery/query/SearchFilterBinaryOperation.scala12
-rw-r--r--src/main/scala/xyz/driver/restquery/query/SearchFilterExpr.scala25
-rw-r--r--src/main/scala/xyz/driver/restquery/query/SearchFilterNAryOperation.scala7
-rw-r--r--src/main/scala/xyz/driver/restquery/query/Sorting.scala4
-rw-r--r--src/main/scala/xyz/driver/restquery/rest/Directives.scala15
-rw-r--r--src/main/scala/xyz/driver/restquery/rest/parsers/DimensionsParser.scala2
-rw-r--r--src/main/scala/xyz/driver/restquery/rest/parsers/PaginationParser.scala9
-rw-r--r--src/main/scala/xyz/driver/restquery/rest/parsers/ParseQueryArgException.scala2
-rw-r--r--src/main/scala/xyz/driver/restquery/rest/parsers/SearchFilterParser.scala6
-rw-r--r--src/main/scala/xyz/driver/restquery/rest/parsers/SortingParser.scala11
-rw-r--r--src/main/scala/xyz/driver/restquery/utils/Utils.scala3
-rw-r--r--src/test/scala/xyz/driver/restquery/db/SearchFilterExprSuite.scala3
-rw-r--r--src/test/scala/xyz/driver/restquery/rest/parsers/PaginationParserSuite.scala6
-rw-r--r--src/test/scala/xyz/driver/restquery/rest/parsers/SearchFilterParserSuite.scala24
-rw-r--r--src/test/scala/xyz/driver/restquery/rest/parsers/SortingParserSuite.scala4
-rw-r--r--src/test/scala/xyz/driver/restquery/rest/parsers/TestUtils.scala5
-rw-r--r--src/test/scala/xyz/driver/restquery/utils/StringOpsSuite.scala15
24 files changed, 171 insertions, 421 deletions
diff --git a/README.md b/README.md
index 74faee6..a0d1c11 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,19 @@
-# PDS UI Common Library
+# REST Query library
-To make a change, create a PR, merge to master, do `sbt scalafmt`, follow instructions.
+Allows to read queries from the REST requests (query parameters) and map them to the typed SQL Slick queries.
+Supports ANDs and ORs, pagination and sorting.
+
+Queries can be build in the code as well (e.g., for testing):
+
+```
+SearchFilterExpr.Atom.NAry("id", In, objects.map(x => Long.box(x.id)))
+```
+
+or
+
+```
+SearchFilterExpr.Union(Seq(
+ SearchFilterExpr.Atom.Binary("status", Eq, "New"),
+ SearchFilterExpr.Atom.Binary("status", Eq, "Verified")
+)
+```
diff --git a/build.sbt b/build.sbt
index 8f2198d..0ca1f7d 100644
--- a/build.sbt
+++ b/build.sbt
@@ -2,26 +2,17 @@ import sbt._
import Keys._
lazy val core = (project in file("."))
- .driverLibrary("pds-ui-common")
- .settings(lintingSettings)
- .settings(scalacOptions -= "-Xfatal-warnings") // this is needed to ignore unused implicits that are actually used in scala 2.11
+ .driverLibrary("rest-query")
+ .settings(lintingSettings ++ formatSettings)
.settings(sources in (Compile, doc) := Seq.empty, publishArtifact in (Compile, packageDoc) := false)
.settings(libraryDependencies ++= Seq(
- "com.github.pureconfig" %% "pureconfig" % "0.7.2",
+ "com.typesafe.akka" %% "akka-http" % "10.0.10",
"com.lihaoyi" %% "fastparse" % "1.0.0",
- "com.typesafe.akka" %% "akka-http" % "10.0.10",
- "com.typesafe.scala-logging" %% "scala-logging" % "3.5.0",
- "io.github.cloudify" %% "spdf" % "1.4.0",
"xyz.driver" %% "core" % "1.6.12",
- "xyz.driver" %% "domain-model" % "0.21.16",
+ "com.typesafe.scala-logging" %% "scala-logging" % "3.5.0",
+ "org.slf4j" % "slf4j-api" % "1.7.21",
"ch.qos.logback" % "logback-classic" % "1.1.7",
- "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.8.4",
- "com.github.spullara.mustache.java" % "scala-extensions-2.11" % "0.9.4",
- "com.google.cloud" % "google-cloud-storage" % "1.2.1",
- "com.sendgrid" % "sendgrid-java" % "3.1.0" exclude ("org.mockito", "mockito-core"),
"com.typesafe" % "config" % "1.3.0",
- "org.asynchttpclient" % "async-http-client" % "2.0.24",
- "org.slf4j" % "slf4j-api" % "1.7.21",
"org.scalacheck" %% "scalacheck" % "1.13.4" % "test",
"org.scalatest" %% "scalatest" % "3.0.1" % "test"
))
diff --git a/project/build.properties b/project/build.properties
index b7dd3cb..9abea12 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=1.0.2
+sbt.version=1.0.3
diff --git a/src/main/scala/xyz/driver/restquery/db/SlickPostgresQueryBuilder.scala b/src/main/scala/xyz/driver/restquery/db/SlickPostgresQueryBuilder.scala
index 7038aa0..0b3fece 100644
--- a/src/main/scala/xyz/driver/restquery/db/SlickPostgresQueryBuilder.scala
+++ b/src/main/scala/xyz/driver/restquery/db/SlickPostgresQueryBuilder.scala
@@ -1,10 +1,11 @@
-package xyz.driver.pdsuicommon.db
+package xyz.driver.restquery.db
import java.time.{LocalDateTime, ZoneOffset}
import org.slf4j.LoggerFactory
import slick.jdbc.{GetResult, JdbcProfile}
import xyz.driver.core.database.SlickDal
+import xyz.driver.restquery.query.{Pagination, SearchFilterExpr, Sorting}
import scala.collection.breakOut
import scala.concurrent.ExecutionContext
@@ -12,18 +13,20 @@ import scala.concurrent.ExecutionContext
object SlickPostgresQueryBuilder {
private val logger = LoggerFactory.getLogger(this.getClass)
- import xyz.driver.pdsuicommon.db.SlickQueryBuilder._
-
- def apply[T](databaseName: String,
- tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[SlickTableLink],
- runner: Runner[T],
- countRunner: CountRunner)(implicit sqlContext: SlickDal,
- profile: JdbcProfile,
- getResult: GetResult[T],
- ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
+ import xyz.driver.restquery.db.SlickQueryBuilder._
+
+ def apply[T](
+ databaseName: String,
+ tableName: String,
+ lastUpdateFieldName: Option[String],
+ nullableFields: Set[String],
+ links: Set[SlickTableLink],
+ runner: Runner[T],
+ countRunner: CountRunner)(
+ implicit sqlContext: SlickDal,
+ profile: JdbcProfile,
+ getResult: GetResult[T],
+ ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
val parameters = SlickPostgresQueryBuilderParameters(
databaseName = databaseName,
tableData = TableData(tableName, lastUpdateFieldName, nullableFields),
@@ -32,31 +35,30 @@ object SlickPostgresQueryBuilder {
new SlickPostgresQueryBuilder[T](parameters)(runner, countRunner)
}
- def apply[T](databaseName: String,
- tableName: String,
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[SlickTableLink])(implicit sqlContext: SlickDal,
- profile: JdbcProfile,
- getResult: GetResult[T],
- ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
- apply[T](databaseName,
- tableName,
- SlickQueryBuilderParameters.AllFields,
- lastUpdateFieldName,
- nullableFields,
- links)
+ def apply[T](
+ databaseName: String,
+ tableName: String,
+ lastUpdateFieldName: Option[String],
+ nullableFields: Set[String],
+ links: Set[SlickTableLink])(
+ implicit sqlContext: SlickDal,
+ profile: JdbcProfile,
+ getResult: GetResult[T],
+ ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
+ apply[T](databaseName, tableName, SlickQueryBuilderParameters.AllFields, lastUpdateFieldName, nullableFields, links)
}
- def apply[T](databaseName: String,
- tableName: String,
- fields: Set[String],
- lastUpdateFieldName: Option[String],
- nullableFields: Set[String],
- links: Set[SlickTableLink])(implicit sqlContext: SlickDal,
- profile: JdbcProfile,
- getResult: GetResult[T],
- ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
+ def apply[T](
+ databaseName: String,
+ tableName: String,
+ fields: Set[String],
+ lastUpdateFieldName: Option[String],
+ nullableFields: Set[String],
+ links: Set[SlickTableLink])(
+ implicit sqlContext: SlickDal,
+ profile: JdbcProfile,
+ getResult: GetResult[T],
+ ec: ExecutionContext): SlickPostgresQueryBuilder[T] = {
val runner: Runner[T] = { parameters =>
val sql = parameters.toSql(countQuery = false, fields = fields).as[T]
@@ -94,8 +96,8 @@ object SlickPostgresQueryBuilder {
}
class SlickPostgresQueryBuilder[T](parameters: SlickPostgresQueryBuilderParameters)(
- implicit runner: SlickQueryBuilder.Runner[T],
- countRunner: SlickQueryBuilder.CountRunner)
+ implicit runner: SlickQueryBuilder.Runner[T],
+ countRunner: SlickQueryBuilder.CountRunner)
extends SlickQueryBuilder[T](parameters) {
def withFilter(newFilter: SearchFilterExpr): SlickQueryBuilder[T] = {
diff --git a/src/main/scala/xyz/driver/restquery/db/SlickQueryBuilder.scala b/src/main/scala/xyz/driver/restquery/db/SlickQueryBuilder.scala
index 9962edf..67ce9f4 100644
--- a/src/main/scala/xyz/driver/restquery/db/SlickQueryBuilder.scala
+++ b/src/main/scala/xyz/driver/restquery/db/SlickQueryBuilder.scala
@@ -1,12 +1,10 @@
-package xyz.driver.pdsuicommon.db
+package xyz.driver.restquery.db
import java.sql.{JDBCType, PreparedStatement}
import java.time.LocalDateTime
import slick.jdbc.{JdbcProfile, PositionedParameters, SQLActionBuilder, SetParameter}
-import xyz.driver.pdsuicommon.db.Sorting.{Dimension, Sequential}
-import xyz.driver.pdsuicommon.db.SortingOrder.{Ascending, Descending}
-import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId}
+import xyz.driver.restquery.query._
import scala.concurrent.{ExecutionContext, Future}
@@ -23,19 +21,18 @@ object SlickQueryBuilder {
*/
type Binder = PreparedStatement => PreparedStatement
- final case class TableData(tableName: String,
- lastUpdateFieldName: Option[String] = None,
- nullableFields: Set[String] = Set.empty)
+ final case class TableData(
+ tableName: String,
+ lastUpdateFieldName: Option[String] = None,
+ nullableFields: Set[String] = Set.empty)
val AllFields = Set("*")
implicit class SQLActionBuilderConcat(a: SQLActionBuilder) {
def concat(b: SQLActionBuilder): SQLActionBuilder = {
- SQLActionBuilder(a.queryParts ++ b.queryParts, new SetParameter[Unit] {
- def apply(p: Unit, pp: PositionedParameters): Unit = {
- a.unitPConv.apply(p, pp)
- b.unitPConv.apply(p, pp)
- }
+ SQLActionBuilder(a.queryParts ++ b.queryParts, (p: Unit, pp: PositionedParameters) => {
+ a.unitPConv.apply(p, pp)
+ b.unitPConv.apply(p, pp)
})
}
}
@@ -45,264 +42,17 @@ object SlickQueryBuilder {
pp.setObject(v, JDBCType.BINARY.getVendorTypeNumber)
}
}
-
- implicit def setLongIdQueryParameter[T]: SetParameter[LongId[T]] = SetParameter[LongId[T]] { (v, pp) =>
- pp.setLong(v.id)
- }
-
- implicit def setStringIdQueryParameter[T]: SetParameter[StringId[T]] = SetParameter[StringId[T]] { (v, pp) =>
- pp.setString(v.id)
- }
-
- implicit def setUuidIdQueryParameter[T]: SetParameter[UuidId[T]] = SetParameter[UuidId[T]] { (v, pp) =>
- pp.setObject(v.id, JDBCType.BINARY.getVendorTypeNumber)
- }
}
final case class SlickTableLink(keyColumnName: String, foreignTableName: String, foreignKeyColumnName: String)
-object SlickQueryBuilderParameters {
- val AllFields = Set("*")
-}
-
-sealed trait SlickQueryBuilderParameters {
- import SlickQueryBuilder._
-
- def databaseName: String
- def tableData: SlickQueryBuilder.TableData
- def links: Map[String, SlickTableLink]
- def filter: SearchFilterExpr
- def sorting: Sorting
- def pagination: Option[Pagination]
-
- def qs: String
-
- def findLink(tableName: String): SlickTableLink = 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)(implicit profile: JdbcProfile): SQLActionBuilder = {
- toSql(countQuery, SlickQueryBuilderParameters.AllFields)
- }
-
- def toSql(countQuery: Boolean, fields: Set[String])(implicit profile: JdbcProfile): SQLActionBuilder = {
- import profile.api._
- val escapedTableName = s"""$qs$databaseName$qs.$qs${tableData.tableName}$qs"""
- val fieldsSql: String = if (countQuery) {
- val suffix: String = tableData.lastUpdateFieldName match {
- case Some(lastUpdateField) => s", max($escapedTableName.$qs$lastUpdateField$qs)"
- case None => ""
- }
- s"count(*) $suffix"
- } else {
- if (fields == SlickQueryBuilderParameters.AllFields) {
- s"$escapedTableName.*"
- } else {
- fields
- .map { field =>
- s"$escapedTableName.$qs$field$qs"
- }
- .mkString(", ")
- }
- }
- val where = filterToSql(escapedTableName, filter)
- val orderBy = sortingToSql(escapedTableName, sorting)
-
- val limitSql = limitToSql()
-
- val sql = sql"""select #$fieldsSql from #$escapedTableName"""
-
- val filtersTableLinks: Seq[SlickTableLink] = {
- import SearchFilterExpr._
- def aux(expr: SearchFilterExpr): Seq[SlickTableLink] = 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[SlickTableLink] = Sorting.collect(sorting) {
- case Dimension(Some(foreignTableName), _, _) => findLink(foreignTableName)
- }
-
- // Combine links from sorting and filter without duplicates
- val foreignTableLinks = (filtersTableLinks ++ sortingTableLinks).distinct
-
- def fkSql(fkLinksSql: SQLActionBuilder, tableLinks: Seq[SlickTableLink]): SQLActionBuilder = {
- if (tableLinks.nonEmpty) {
- tableLinks.head match {
- case SlickTableLink(keyColumnName, foreignTableName, foreignKeyColumnName) =>
- val escapedForeignTableName = s"$qs$databaseName$qs.$qs$foreignTableName$qs"
- val join = sql""" inner join #$escapedForeignTableName
- on #$escapedTableName.#$qs#$keyColumnName#$qs=#$escapedForeignTableName.#$qs#$foreignKeyColumnName#$qs"""
- fkSql(fkLinksSql concat join, tableLinks.tail)
- }
- } else fkLinksSql
- }
- val foreignTableLinksSql = fkSql(sql"", foreignTableLinks)
-
- val whereSql = if (where.queryParts.size > 1) {
- sql" where " concat where
- } else sql""
-
- val orderSql = if (orderBy.nonEmpty && !countQuery) {
- sql" order by #$orderBy"
- } else sql""
-
- val limSql = if (limitSql.queryParts.size > 1 && !countQuery) {
- sql" " concat limitSql
- } else sql""
-
- sql concat foreignTableLinksSql concat whereSql concat orderSql concat limSql
- }
-
- /**
- * 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)(
- implicit profile: JdbcProfile): SQLActionBuilder = {
- import SearchFilterBinaryOperation._
- import SearchFilterExpr._
- import profile.api._
-
- def isNull(string: AnyRef) = Option(string).isEmpty || string.toString.toLowerCase == "null"
-
- def escapeDimension(dimension: SearchFilterExpr.Dimension) = {
- s"${dimension.tableName.map(t => s"$qs$databaseName$qs.$qs$t$qs").getOrElse(escapedTableName)}.$qs${dimension.name}$qs"
- }
-
- def filterToSqlMultiple(operands: Seq[SearchFilterExpr]) = operands.collect {
- case x if !SearchFilterExpr.isEmpty(x) => filterToSql(escapedTableName, x)
- }
-
- def multipleSqlToAction(first: Boolean,
- op: String,
- conditions: Seq[SQLActionBuilder],
- sql: SQLActionBuilder): SQLActionBuilder = {
- if (conditions.nonEmpty) {
- val condition = conditions.head
- if (first) {
- multipleSqlToAction(first = false, op, conditions.tail, condition)
- } else {
- multipleSqlToAction(first = false, op, conditions.tail, sql concat sql" #${op} " concat condition)
- }
- } else sql
- }
-
- def concatenateParameters(sql: SQLActionBuilder, first: Boolean, tail: Seq[AnyRef]): SQLActionBuilder = {
- if (tail.nonEmpty) {
- if (!first) {
- concatenateParameters(sql concat sql""",${tail.head}""", first = false, tail.tail)
- } else {
- concatenateParameters(sql"""(${tail.head}""", first = false, tail.tail)
- }
- } else sql concat sql")"
- }
-
- filter match {
- case x if isEmpty(x) =>
- sql""
-
- case AllowAll =>
- sql"1=1"
-
- case DenyAll =>
- sql"1=0"
-
- case Atom.Binary(dimension, Eq, value) if isNull(value) =>
- sql"#${escapeDimension(dimension)} is NULL"
-
- case Atom.Binary(dimension, NotEq, value) if isNull(value) =>
- sql"#${escapeDimension(dimension)} is not NULL"
-
- 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)
- sql"(#${escapedColumn} is null or #${escapedColumn} != $value)"
-
- case Atom.Binary(dimension, op, value) =>
- val operator = op match {
- case Eq => sql"="
- case NotEq => sql"!="
- case Like => sql" like "
- case Gt => sql">"
- case GtEq => sql">="
- case Lt => sql"<"
- case LtEq => sql"<="
- }
- sql"#${escapeDimension(dimension)}" concat operator concat sql"""$value"""
-
- case Atom.NAry(dimension, op, values) =>
- val sqlOp = op match {
- case SearchFilterNAryOperation.In => sql" in "
- case SearchFilterNAryOperation.NotIn => sql" not in "
- }
-
- if (values.nonEmpty) {
- val formattedValues = concatenateParameters(sql"", first = true, values)
- sql"#${escapeDimension(dimension)}" concat sqlOp concat formattedValues
- } else {
- sql"1=0"
- }
-
- case Intersection(operands) =>
- val filter = multipleSqlToAction(first = true, "and", filterToSqlMultiple(operands), sql"")
- sql"(" concat filter concat sql")"
-
- case Union(operands) =>
- val filter = multipleSqlToAction(first = true, "or", filterToSqlMultiple(operands), sql"")
- sql"(" concat filter concat sql")"
- }
- }
-
- protected def limitToSql()(implicit profile: JdbcProfile): SQLActionBuilder
-
- /**
- * @param escapedMainTableName Should be escaped
- */
- protected def sortingToSql(escapedMainTableName: String, sorting: Sorting)(implicit profile: JdbcProfile): String = {
- sorting match {
- case Dimension(optSortingTableName, field, order) =>
- val sortingTableName =
- optSortingTableName.map(x => s"$qs$databaseName$qs.$qs$x$qs").getOrElse(escapedMainTableName)
- val fullName = s"$sortingTableName.$qs$field$qs"
-
- s"$fullName ${orderToSql(order)}"
-
- case Sequential(xs) =>
- xs.map(sortingToSql(escapedMainTableName, _)).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 SlickPostgresQueryBuilderParameters(databaseName: String,
- tableData: SlickQueryBuilder.TableData,
- links: Map[String, SlickTableLink] = Map.empty,
- filter: SearchFilterExpr = SearchFilterExpr.Empty,
- sorting: Sorting = Sorting.Empty,
- pagination: Option[Pagination] = None)
+final case class SlickPostgresQueryBuilderParameters(
+ databaseName: String,
+ tableData: SlickQueryBuilder.TableData,
+ links: Map[String, SlickTableLink] = Map.empty,
+ filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Sorting = Sorting.Empty,
+ pagination: Option[Pagination] = None)
extends SlickQueryBuilderParameters {
def limitToSql()(implicit profile: JdbcProfile): SQLActionBuilder = {
@@ -320,12 +70,13 @@ final case class SlickPostgresQueryBuilderParameters(databaseName: String,
/**
* @param links Links to another tables grouped by foreignTableName
*/
-final case class SlickMysqlQueryBuilderParameters(databaseName: String,
- tableData: SlickQueryBuilder.TableData,
- links: Map[String, SlickTableLink] = Map.empty,
- filter: SearchFilterExpr = SearchFilterExpr.Empty,
- sorting: Sorting = Sorting.Empty,
- pagination: Option[Pagination] = None)
+final case class SlickMysqlQueryBuilderParameters(
+ databaseName: String,
+ tableData: SlickQueryBuilder.TableData,
+ links: Map[String, SlickTableLink] = Map.empty,
+ filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Sorting = Sorting.Empty,
+ pagination: Option[Pagination] = None)
extends SlickQueryBuilderParameters {
def limitToSql()(implicit profile: JdbcProfile): SQLActionBuilder = {
@@ -343,8 +94,8 @@ final case class SlickMysqlQueryBuilderParameters(databaseName: String,
}
abstract class SlickQueryBuilder[T](val parameters: SlickQueryBuilderParameters)(
- implicit runner: SlickQueryBuilder.Runner[T],
- countRunner: SlickQueryBuilder.CountRunner) {
+ implicit runner: SlickQueryBuilder.Runner[T],
+ countRunner: SlickQueryBuilder.CountRunner) {
def run()(implicit ec: ExecutionContext): Future[Seq[T]] = runner(parameters)
diff --git a/src/main/scala/xyz/driver/restquery/db/SlickQueryBuilderParameters.scala b/src/main/scala/xyz/driver/restquery/db/SlickQueryBuilderParameters.scala
index 6ab7eb4..d4d1761 100644
--- a/src/main/scala/xyz/driver/restquery/db/SlickQueryBuilderParameters.scala
+++ b/src/main/scala/xyz/driver/restquery/db/SlickQueryBuilderParameters.scala
@@ -1,11 +1,15 @@
-package xyz.driver.restquery.query
+package xyz.driver.restquery.db
import java.sql.PreparedStatement
import slick.jdbc.{JdbcProfile, SQLActionBuilder}
-import xyz.driver.restquery.db.{SlickQueryBuilder, SlickQueryBuilderParameters, SlickTableLink}
import xyz.driver.restquery.query.Sorting.{Dimension, Sequential}
import xyz.driver.restquery.query.SortingOrder.{Ascending, Descending}
+import xyz.driver.restquery.query._
+
+object SlickQueryBuilderParameters {
+ val AllFields = Set("*")
+}
trait SlickQueryBuilderParameters {
import SlickQueryBuilder._
@@ -107,7 +111,7 @@ trait SlickQueryBuilderParameters {
* @return Returns SQL string and list of values for binding in prepared statement.
*/
protected def filterToSql(escapedTableName: String, filter: SearchFilterExpr)(
- implicit profile: JdbcProfile): SQLActionBuilder = {
+ implicit profile: JdbcProfile): SQLActionBuilder = {
import SearchFilterBinaryOperation._
import SearchFilterExpr._
import profile.api._
@@ -122,10 +126,11 @@ trait SlickQueryBuilderParameters {
case x if !SearchFilterExpr.isEmpty(x) => filterToSql(escapedTableName, x)
}
- def multipleSqlToAction(first: Boolean,
- op: String,
- conditions: Seq[SQLActionBuilder],
- sql: SQLActionBuilder): SQLActionBuilder = {
+ def multipleSqlToAction(
+ first: Boolean,
+ op: String,
+ conditions: Seq[SQLActionBuilder],
+ sql: SQLActionBuilder): SQLActionBuilder = {
if (conditions.nonEmpty) {
val condition = conditions.head
if (first) {
diff --git a/src/main/scala/xyz/driver/restquery/query/Pagination.scala b/src/main/scala/xyz/driver/restquery/query/Pagination.scala
index 27b8f12..b899d1b 100644
--- a/src/main/scala/xyz/driver/restquery/query/Pagination.scala
+++ b/src/main/scala/xyz/driver/restquery/query/Pagination.scala
@@ -1,4 +1,4 @@
-package xyz.driver.restquery.domain
+package xyz.driver.restquery.query
/**
* @param pageNumber Starts with 1
diff --git a/src/main/scala/xyz/driver/restquery/query/SearchFilterBinaryOperation.scala b/src/main/scala/xyz/driver/restquery/query/SearchFilterBinaryOperation.scala
index dab466b..30d210c 100644
--- a/src/main/scala/xyz/driver/restquery/query/SearchFilterBinaryOperation.scala
+++ b/src/main/scala/xyz/driver/restquery/query/SearchFilterBinaryOperation.scala
@@ -1,5 +1,15 @@
package xyz.driver.restquery.query
-class SearchFilterBinaryOperation {
+sealed trait SearchFilterBinaryOperation
+
+object SearchFilterBinaryOperation {
+
+ case object Eq extends SearchFilterBinaryOperation
+ case object NotEq extends SearchFilterBinaryOperation
+ case object Like extends SearchFilterBinaryOperation
+ case object Gt extends SearchFilterBinaryOperation
+ case object GtEq extends SearchFilterBinaryOperation
+ case object Lt extends SearchFilterBinaryOperation
+ case object LtEq extends SearchFilterBinaryOperation
}
diff --git a/src/main/scala/xyz/driver/restquery/query/SearchFilterExpr.scala b/src/main/scala/xyz/driver/restquery/query/SearchFilterExpr.scala
index 6d2cb9a..8cbf685 100644
--- a/src/main/scala/xyz/driver/restquery/query/SearchFilterExpr.scala
+++ b/src/main/scala/xyz/driver/restquery/query/SearchFilterExpr.scala
@@ -1,4 +1,4 @@
-package xyz.driver.restquery.domain
+package xyz.driver.restquery.query
sealed trait SearchFilterExpr {
def find(p: SearchFilterExpr => Boolean): Option[SearchFilterExpr]
@@ -171,26 +171,3 @@ object SearchFilterExpr {
}
}
-
-sealed trait SearchFilterBinaryOperation
-
-object SearchFilterBinaryOperation {
-
- case object Eq extends SearchFilterBinaryOperation
- case object NotEq extends SearchFilterBinaryOperation
- case object Like extends SearchFilterBinaryOperation
- case object Gt extends SearchFilterBinaryOperation
- case object GtEq extends SearchFilterBinaryOperation
- case object Lt extends SearchFilterBinaryOperation
- case object LtEq extends SearchFilterBinaryOperation
-
-}
-
-sealed trait SearchFilterNAryOperation
-
-object SearchFilterNAryOperation {
-
- case object In extends SearchFilterNAryOperation
- case object NotIn extends SearchFilterNAryOperation
-
-}
diff --git a/src/main/scala/xyz/driver/restquery/query/SearchFilterNAryOperation.scala b/src/main/scala/xyz/driver/restquery/query/SearchFilterNAryOperation.scala
index 0604c8f..a388597 100644
--- a/src/main/scala/xyz/driver/restquery/query/SearchFilterNAryOperation.scala
+++ b/src/main/scala/xyz/driver/restquery/query/SearchFilterNAryOperation.scala
@@ -1,5 +1,10 @@
package xyz.driver.restquery.query
-class SearchFilterNAryOperation {
+sealed trait SearchFilterNAryOperation
+
+object SearchFilterNAryOperation {
+
+ case object In extends SearchFilterNAryOperation
+ case object NotIn extends SearchFilterNAryOperation
}
diff --git a/src/main/scala/xyz/driver/restquery/query/Sorting.scala b/src/main/scala/xyz/driver/restquery/query/Sorting.scala
index e2642ad..af0066b 100644
--- a/src/main/scala/xyz/driver/restquery/query/Sorting.scala
+++ b/src/main/scala/xyz/driver/restquery/query/Sorting.scala
@@ -1,4 +1,4 @@
-package xyz.driver.restquery.domain
+package xyz.driver.restquery.query
import scala.collection.generic.CanBuildFrom
@@ -43,7 +43,7 @@ object Sorting {
}
def collect[B, That](sorting: Sorting)(f: PartialFunction[Dimension, B])(
- implicit bf: CanBuildFrom[Seq[Dimension], B, That]): That = sorting match {
+ implicit bf: CanBuildFrom[Seq[Dimension], B, That]): That = sorting match {
case x: Dimension if f.isDefinedAt(x) =>
val r = bf.apply()
r += f(x)
diff --git a/src/main/scala/xyz/driver/restquery/rest/Directives.scala b/src/main/scala/xyz/driver/restquery/rest/Directives.scala
index 2936f70..a8dcb3b 100644
--- a/src/main/scala/xyz/driver/restquery/rest/Directives.scala
+++ b/src/main/scala/xyz/driver/restquery/rest/Directives.scala
@@ -1,9 +1,9 @@
-package xyz.driver.restquery.http
+package xyz.driver.restquery.rest
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server._
-import xyz.driver.restquery.domain.{SearchFilterExpr, _}
-import xyz.driver.restquery.http.parsers._
+import xyz.driver.restquery.query.{SearchFilterExpr, _}
+import xyz.driver.restquery.rest.parsers._
import scala.util._
@@ -41,15 +41,6 @@ trait Directives {
}
}
- def StringIdInPath[T]: PathMatcher1[StringId[T]] =
- PathMatchers.Segment.map((id) => StringId(id.toString))
-
- def LongIdInPath[T]: PathMatcher1[LongId[T]] =
- PathMatchers.LongNumber.map((id) => LongId(id))
-
- def UuidIdInPath[T]: PathMatcher1[UuidId[T]] =
- PathMatchers.JavaUUID.map((id) => UuidId(id))
-
}
object Directives extends Directives
diff --git a/src/main/scala/xyz/driver/restquery/rest/parsers/DimensionsParser.scala b/src/main/scala/xyz/driver/restquery/rest/parsers/DimensionsParser.scala
index 7e139db..3472e40 100644
--- a/src/main/scala/xyz/driver/restquery/rest/parsers/DimensionsParser.scala
+++ b/src/main/scala/xyz/driver/restquery/rest/parsers/DimensionsParser.scala
@@ -1,4 +1,4 @@
-package xyz.driver.restquery.http.parsers
+package xyz.driver.restquery.rest.parsers
import scala.util.{Failure, Success, Try}
diff --git a/src/main/scala/xyz/driver/restquery/rest/parsers/PaginationParser.scala b/src/main/scala/xyz/driver/restquery/rest/parsers/PaginationParser.scala
index 2b4547b..6d8aaf8 100644
--- a/src/main/scala/xyz/driver/restquery/rest/parsers/PaginationParser.scala
+++ b/src/main/scala/xyz/driver/restquery/rest/parsers/PaginationParser.scala
@@ -1,6 +1,6 @@
-package xyz.driver.restquery.http.parsers
+package xyz.driver.restquery.rest.parsers
-import xyz.driver.restquery.domain.Pagination
+import xyz.driver.restquery.query.Pagination
import scala.util._
@@ -16,8 +16,9 @@ object PaginationParser {
}
Try {
- Pagination(validate("pageSize", Pagination.Default.pageSize),
- validate("pageNumber", Pagination.Default.pageNumber))
+ Pagination(
+ validate("pageSize", Pagination.Default.pageSize),
+ validate("pageNumber", Pagination.Default.pageNumber))
}
}
}
diff --git a/src/main/scala/xyz/driver/restquery/rest/parsers/ParseQueryArgException.scala b/src/main/scala/xyz/driver/restquery/rest/parsers/ParseQueryArgException.scala
index 096c28f..456329e 100644
--- a/src/main/scala/xyz/driver/restquery/rest/parsers/ParseQueryArgException.scala
+++ b/src/main/scala/xyz/driver/restquery/rest/parsers/ParseQueryArgException.scala
@@ -1,3 +1,3 @@
-package xyz.driver.restquery.http.parsers
+package xyz.driver.restquery.rest.parsers
class ParseQueryArgException(val errors: (String, String)*) extends Exception(errors.mkString(","))
diff --git a/src/main/scala/xyz/driver/restquery/rest/parsers/SearchFilterParser.scala b/src/main/scala/xyz/driver/restquery/rest/parsers/SearchFilterParser.scala
index ce3009b..52a31bf 100644
--- a/src/main/scala/xyz/driver/restquery/rest/parsers/SearchFilterParser.scala
+++ b/src/main/scala/xyz/driver/restquery/rest/parsers/SearchFilterParser.scala
@@ -1,10 +1,10 @@
-package xyz.driver.restquery.http.parsers
+package xyz.driver.restquery.rest.parsers
import java.util.UUID
import fastparse.all._
import fastparse.core.Parsed
-import xyz.driver.restquery.domain.{SearchFilterBinaryOperation, SearchFilterExpr, SearchFilterNAryOperation}
+import xyz.driver.restquery.query.{SearchFilterBinaryOperation, SearchFilterExpr, SearchFilterNAryOperation}
import xyz.driver.restquery.utils.Utils
import xyz.driver.restquery.utils.Utils._
@@ -47,7 +47,7 @@ object SearchFilterParser {
}
private val operationsMapping = {
- import xyz.driver.restquery.domain.SearchFilterBinaryOperation._
+ import xyz.driver.restquery.query.SearchFilterBinaryOperation._
Map[String, SearchFilterBinaryOperation](
"eq" -> Eq,
diff --git a/src/main/scala/xyz/driver/restquery/rest/parsers/SortingParser.scala b/src/main/scala/xyz/driver/restquery/rest/parsers/SortingParser.scala
index f2a3c04..fce18d1 100644
--- a/src/main/scala/xyz/driver/restquery/rest/parsers/SortingParser.scala
+++ b/src/main/scala/xyz/driver/restquery/rest/parsers/SortingParser.scala
@@ -1,8 +1,8 @@
-package xyz.driver.restquery.http.parsers
+package xyz.driver.restquery.rest.parsers
import fastparse.all._
import fastparse.core.Parsed
-import xyz.driver.restquery.domain.{Sorting, SortingOrder}
+import xyz.driver.restquery.query.{Sorting, SortingOrder}
import xyz.driver.restquery.utils.Utils._
import scala.util.Try
@@ -21,9 +21,10 @@ object SortingParser {
prefixedFields.size match {
case 1 => Sorting.Dimension(None, toSnakeCase(field), sortingOrder)
case 2 =>
- Sorting.Dimension(Some(prefixedFields.head).map(toSnakeCase),
- toSnakeCase(prefixedFields.last),
- sortingOrder)
+ Sorting.Dimension(
+ Some(prefixedFields.head).map(toSnakeCase),
+ toSnakeCase(prefixedFields.last),
+ sortingOrder)
}
}
}
diff --git a/src/main/scala/xyz/driver/restquery/utils/Utils.scala b/src/main/scala/xyz/driver/restquery/utils/Utils.scala
index 86c65d7..827f2a0 100644
--- a/src/main/scala/xyz/driver/restquery/utils/Utils.scala
+++ b/src/main/scala/xyz/driver/restquery/utils/Utils.scala
@@ -1,4 +1,4 @@
-package xyz.driver.pdsuicommon.utils
+package xyz.driver.restquery.utils
import java.time.LocalDateTime
import java.util.regex.{Matcher, Pattern}
@@ -59,7 +59,6 @@ object Utils {
def isSafeControl(char: Char): Boolean =
char <= '\u001f' || (char >= '\u007f' && char <= '\u009f')
-
def safeTrim(string: String): String = {
def shouldKeep(c: Char): Boolean = !isSafeControl(c) && !isSafeWhitespace(c)
diff --git a/src/test/scala/xyz/driver/restquery/db/SearchFilterExprSuite.scala b/src/test/scala/xyz/driver/restquery/db/SearchFilterExprSuite.scala
index 35c8d30..56ad545 100644
--- a/src/test/scala/xyz/driver/restquery/db/SearchFilterExprSuite.scala
+++ b/src/test/scala/xyz/driver/restquery/db/SearchFilterExprSuite.scala
@@ -1,6 +1,7 @@
-package xyz.driver.pdsuicommon.db
+package xyz.driver.restquery.db
import org.scalatest.{FreeSpecLike, MustMatchers}
+import xyz.driver.restquery.query.{SearchFilterBinaryOperation, SearchFilterExpr, SearchFilterNAryOperation}
class SearchFilterExprSuite extends FreeSpecLike with MustMatchers {
diff --git a/src/test/scala/xyz/driver/restquery/rest/parsers/PaginationParserSuite.scala b/src/test/scala/xyz/driver/restquery/rest/parsers/PaginationParserSuite.scala
index c753480..e806f85 100644
--- a/src/test/scala/xyz/driver/restquery/rest/parsers/PaginationParserSuite.scala
+++ b/src/test/scala/xyz/driver/restquery/rest/parsers/PaginationParserSuite.scala
@@ -1,8 +1,8 @@
-package xyz.driver.restquery.http.parsers
+package xyz.driver.restquery.rest.parsers
-import xyz.driver.restquery.http.parsers.TestUtils._
+import xyz.driver.restquery.rest.parsers.TestUtils._
import org.scalatest.{FreeSpecLike, MustMatchers}
-import xyz.driver.restquery.domain.Pagination
+import xyz.driver.restquery.query.Pagination
import scala.util.{Failure, Try}
diff --git a/src/test/scala/xyz/driver/restquery/rest/parsers/SearchFilterParserSuite.scala b/src/test/scala/xyz/driver/restquery/rest/parsers/SearchFilterParserSuite.scala
index efa8666..e0a1696 100644
--- a/src/test/scala/xyz/driver/restquery/rest/parsers/SearchFilterParserSuite.scala
+++ b/src/test/scala/xyz/driver/restquery/rest/parsers/SearchFilterParserSuite.scala
@@ -1,4 +1,4 @@
-package xyz.driver.restquery.http.parsers
+package xyz.driver.restquery.rest.parsers
import java.util.UUID
@@ -7,11 +7,11 @@ import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.{Gen, Prop}
import org.scalatest.FreeSpecLike
import org.scalatest.prop.Checkers
-import xyz.driver.restquery.domain.SearchFilterBinaryOperation.Eq
-import xyz.driver.restquery.domain.SearchFilterExpr.Dimension
-import xyz.driver.restquery.domain.SearchFilterNAryOperation.In
-import xyz.driver.restquery.domain.{SearchFilterExpr, SearchFilterNAryOperation}
-import xyz.driver.restquery.http.parsers.TestUtils._
+import xyz.driver.restquery.query.SearchFilterBinaryOperation.Eq
+import xyz.driver.restquery.query.SearchFilterExpr.Dimension
+import xyz.driver.restquery.query.SearchFilterNAryOperation.In
+import xyz.driver.restquery.query.{SearchFilterExpr, SearchFilterNAryOperation}
+import xyz.driver.restquery.rest.parsers.TestUtils._
import xyz.driver.restquery.utils.Utils
import xyz.driver.restquery.utils.Utils._
@@ -29,7 +29,7 @@ class SearchFilterParserSuite extends FreeSpecLike with Checkers {
"parse" - {
"should convert column names to snake case" in {
- import xyz.driver.restquery.domain.SearchFilterBinaryOperation._
+ import xyz.driver.restquery.query.SearchFilterBinaryOperation._
val filter = SearchFilterParser.parse(
Seq(
@@ -77,7 +77,7 @@ class SearchFilterParserSuite extends FreeSpecLike with Checkers {
"binary" - {
"common operators" - {
"should be parsed with text values" in check {
- import xyz.driver.restquery.domain.SearchFilterBinaryOperation._
+ import xyz.driver.restquery.query.SearchFilterBinaryOperation._
val testQueryGen = queryGen(
dimensionGen = Gen.identifier,
@@ -123,15 +123,17 @@ class SearchFilterParserSuite extends FreeSpecLike with Checkers {
"actual isVisible boolean" - {
"should not be parsed with boolean values" in {
val filter = SearchFilterParser.parse(Seq("filters" -> "isVisible EQ true"))
- assert(filter === Success(SearchFilterExpr.Atom.Binary(Dimension(None, "is_visible"), Eq, Boolean.box(true))))
+ assert(
+ filter === Success(SearchFilterExpr.Atom.Binary(Dimension(None, "is_visible"), Eq, Boolean.box(true))))
}
}
"actual patientId uuid" - {
"should parse the full UUID as java.util.UUID type" in {
val filter = SearchFilterParser.parse(Seq("filters" -> "patientId EQ 4b4879f7-42b3-4b7c-a685-5c97d9e69e7c"))
- assert(filter === Success(SearchFilterExpr.Atom.Binary(
- Dimension(None, "patient_id"), Eq, UUID.fromString("4b4879f7-42b3-4b7c-a685-5c97d9e69e7c"))))
+ assert(
+ filter === Success(SearchFilterExpr.Atom
+ .Binary(Dimension(None, "patient_id"), Eq, UUID.fromString("4b4879f7-42b3-4b7c-a685-5c97d9e69e7c"))))
}
}
diff --git a/src/test/scala/xyz/driver/restquery/rest/parsers/SortingParserSuite.scala b/src/test/scala/xyz/driver/restquery/rest/parsers/SortingParserSuite.scala
index 1813181..a18ac1e 100644
--- a/src/test/scala/xyz/driver/restquery/rest/parsers/SortingParserSuite.scala
+++ b/src/test/scala/xyz/driver/restquery/rest/parsers/SortingParserSuite.scala
@@ -1,6 +1,6 @@
-package xyz.driver.restquery.http.parsers
+package xyz.driver.restquery.rest.parsers
-import xyz.driver.restquery.http.parsers.TestUtils._
+import xyz.driver.restquery.rest.parsers.TestUtils._
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.{Gen, Prop}
import org.scalatest.prop.Checkers
diff --git a/src/test/scala/xyz/driver/restquery/rest/parsers/TestUtils.scala b/src/test/scala/xyz/driver/restquery/rest/parsers/TestUtils.scala
index 9ea75b6..22ce5a0 100644
--- a/src/test/scala/xyz/driver/restquery/rest/parsers/TestUtils.scala
+++ b/src/test/scala/xyz/driver/restquery/rest/parsers/TestUtils.scala
@@ -1,7 +1,6 @@
-package xyz.driver.restquery.http.parsers
+package xyz.driver.restquery.rest.parsers
-import
-org.scalacheck.Prop
+import org.scalacheck.Prop
import org.scalacheck.Prop.BooleanOperators
import org.scalatest.matchers.{MatchResult, Matcher}
import xyz.driver.restquery.utils.Utils
diff --git a/src/test/scala/xyz/driver/restquery/utils/StringOpsSuite.scala b/src/test/scala/xyz/driver/restquery/utils/StringOpsSuite.scala
index bd0bd2b..97a432f 100644
--- a/src/test/scala/xyz/driver/restquery/utils/StringOpsSuite.scala
+++ b/src/test/scala/xyz/driver/restquery/utils/StringOpsSuite.scala
@@ -1,33 +1,32 @@
-package xyz.driver.pdsuicommon.utils
+package xyz.driver.restquery.utils
-import xyz.driver.pdsuicommon.utils.Implicits.toStringOps
import org.scalatest.FreeSpecLike
class StringOpsSuite extends FreeSpecLike {
"safeTrim" - {
"empty string" in {
- assert("".safeTrim == "")
+ assert(Utils.safeTrim("") == "")
}
"string with whitespace symbols" in {
- assert("\u2002\u3000\r\u0085\u200A\u2005\u2000\u3000".safeTrim == "")
+ assert(Utils.safeTrim("\u2002\u3000\r\u0085\u200A\u2005\u2000\u3000") == "")
}
"string with control symbols" in {
- assert("\u001f\u007f\t\n".safeTrim == "")
+ assert(Utils.safeTrim("\u001f\u007f\t\n") == "")
}
"whitespaces and control symbols from the left side" in {
- assert("\u001f\u2002\u007f\nfoo".safeTrim == "foo")
+ assert(Utils.safeTrim("\u001f\u2002\u007f\nfoo") == "foo")
}
"whitespaces and control symbols from the right side" in {
- assert("foo\u001f\u2002\u007f\n".safeTrim == "foo")
+ assert(Utils.safeTrim("foo\u001f\u2002\u007f\n") == "foo")
}
"already trimmed string" in {
- assert("foo".safeTrim == "foo")
+ assert(Utils.safeTrim("foo") == "foo")
}
}
}