From a0877d81ca2844d75dc361b5ce7c99afacd6e25f Mon Sep 17 00:00:00 2001 From: vlad Date: Thu, 25 Jan 2018 14:12:31 -0800 Subject: Extracting query library --- .../pdsuicommon/parsers/DimensionsParser.scala | 30 ---- .../pdsuicommon/parsers/PaginationParser.scala | 29 ---- .../parsers/ParseQueryArgException.scala | 3 - .../pdsuicommon/parsers/SearchFilterParser.scala | 178 --------------------- .../driver/pdsuicommon/parsers/SortingParser.scala | 64 -------- 5 files changed, 304 deletions(-) delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/parsers/DimensionsParser.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/parsers/PaginationParser.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/parsers/ParseQueryArgException.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/parsers/SearchFilterParser.scala delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/parsers/SortingParser.scala (limited to 'src/main/scala/xyz/driver/pdsuicommon/parsers') diff --git a/src/main/scala/xyz/driver/pdsuicommon/parsers/DimensionsParser.scala b/src/main/scala/xyz/driver/pdsuicommon/parsers/DimensionsParser.scala deleted file mode 100644 index 17c09ed..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/parsers/DimensionsParser.scala +++ /dev/null @@ -1,30 +0,0 @@ -package xyz.driver.pdsuicommon.parsers - -import scala.util.{Failure, Success, Try} - -class Dimensions(private val xs: Set[String] = Set.empty) { - def contains(x: String): Boolean = xs.isEmpty || xs.contains(x) -} - -object DimensionsParser { - - @deprecated("play-akka transition", "0") - def tryParse(query: Map[String, Seq[String]]): Try[Dimensions] = - tryParse(query.toSeq.flatMap { - case (key, values) => - values.map(value => key -> value) - }) - - def tryParse(query: Seq[(String, String)]): Try[Dimensions] = { - query.collect { case ("dimensions", value) => value } match { - case Nil => Success(new Dimensions()) - - case x +: Nil => - val raw: Set[String] = x.split(",").view.map(_.trim).filter(_.nonEmpty).to[Set] - Success(new Dimensions(raw)) - - case xs => - Failure(new IllegalArgumentException(s"Dimensions are specified ${xs.size} times")) - } - } -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/parsers/PaginationParser.scala b/src/main/scala/xyz/driver/pdsuicommon/parsers/PaginationParser.scala deleted file mode 100644 index b59b1a5..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/parsers/PaginationParser.scala +++ /dev/null @@ -1,29 +0,0 @@ -package xyz.driver.pdsuicommon.parsers - -import xyz.driver.pdsuicommon.db._ -import scala.util._ - -object PaginationParser { - - @deprecated("play-akka transition", "0") - def parse(query: Map[String, Seq[String]]): Try[Pagination] = - parse(query.toSeq.flatMap { - case (key, values) => - values.map(value => key -> value) - }) - - def parse(query: Seq[(String, String)]): Try[Pagination] = { - val IntString = """(\d+)""".r - def validate(field: String, default: Int) = query.collectFirst { case (`field`, size) => size } match { - case Some(IntString(x)) if x.toInt > 0 => x.toInt - case Some(IntString(x)) => throw new ParseQueryArgException((field, s"must greater than zero (found $x)")) - case Some(str) => throw new ParseQueryArgException((field, s"must be an integer (found $str)")) - case None => default - } - - Try { - Pagination(validate("pageSize", Pagination.Default.pageSize), - validate("pageNumber", Pagination.Default.pageNumber)) - } - } -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/parsers/ParseQueryArgException.scala b/src/main/scala/xyz/driver/pdsuicommon/parsers/ParseQueryArgException.scala deleted file mode 100644 index 64b3d2e..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/parsers/ParseQueryArgException.scala +++ /dev/null @@ -1,3 +0,0 @@ -package xyz.driver.pdsuicommon.parsers - -class ParseQueryArgException(val errors: (String, String)*) extends Exception(errors.mkString(",")) diff --git a/src/main/scala/xyz/driver/pdsuicommon/parsers/SearchFilterParser.scala b/src/main/scala/xyz/driver/pdsuicommon/parsers/SearchFilterParser.scala deleted file mode 100644 index aeb6c16..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/parsers/SearchFilterParser.scala +++ /dev/null @@ -1,178 +0,0 @@ -package xyz.driver.pdsuicommon.parsers - -import java.util.UUID - -import xyz.driver.pdsuicommon.utils.Implicits.{toCharOps, toStringOps} -import fastparse.all._ -import fastparse.core.Parsed -import xyz.driver.pdsuicommon.db.{SearchFilterBinaryOperation, SearchFilterExpr, SearchFilterNAryOperation} -import xyz.driver.pdsuicommon.utils.Utils._ - -import scala.util.Try - -@SuppressWarnings(Array("org.wartremover.warts.Product", "org.wartremover.warts.Serializable")) -object SearchFilterParser { - - private object BinaryAtomFromTuple { - def unapply(input: (SearchFilterExpr.Dimension, (String, Any))): Option[SearchFilterExpr.Atom.Binary] = { - val (dimensionName, (strOperation, value)) = input - val updatedValue = trimIfString(value) - - parseOperation(strOperation.toLowerCase).map { op => - SearchFilterExpr.Atom.Binary(dimensionName, op, updatedValue.asInstanceOf[AnyRef]) - } - } - } - - private object NAryAtomFromTuple { - // Compiler warning: unchecked since it is eliminated by erasure, if we user Seq[String] - def unapply(input: (SearchFilterExpr.Dimension, (String, Seq[_]))): Option[SearchFilterExpr.Atom.NAry] = { - val (dimensionName, (strOperation, xs)) = input - val updatedValues = xs.map(trimIfString) - - if (strOperation.toLowerCase == "in") { - Some( - SearchFilterExpr.Atom - .NAry(dimensionName, SearchFilterNAryOperation.In, updatedValues.map(_.asInstanceOf[AnyRef]))) - } else { - None - } - } - } - - private def trimIfString(value: Any) = - value match { - case s: String => s.safeTrim - case a => a - } - - private val operationsMapping = { - import xyz.driver.pdsuicommon.db.SearchFilterBinaryOperation._ - - Map[String, SearchFilterBinaryOperation]( - "eq" -> Eq, - "noteq" -> NotEq, - "like" -> Like, - "gt" -> Gt, - "gteq" -> GtEq, - "lt" -> Lt, - "lteq" -> LtEq - ) - } - - private def parseOperation(x: String): Option[SearchFilterBinaryOperation] = operationsMapping.get(x) - - private val whitespaceParser = P(CharPred(_.isSafeWhitespace)) - - val dimensionParser: Parser[SearchFilterExpr.Dimension] = { - val identParser = P( - CharPred(c => c.isLetterOrDigit) - .rep(min = 1)).!.map(s => SearchFilterExpr.Dimension(None, toSnakeCase(s))) - val pathParser = P(identParser.! ~ "." ~ identParser.!) map { - case (left, right) => - SearchFilterExpr.Dimension(Some(toSnakeCase(left)), toSnakeCase(right)) - } - P(pathParser | identParser) - } - - private val commonOperatorParser: Parser[String] = { - P(IgnoreCase("eq") | IgnoreCase("like") | IgnoreCase("noteq")).! - } - - private val numericOperatorParser: Parser[String] = { - P(IgnoreCase("eq") | IgnoreCase("noteq") | ((IgnoreCase("gt") | IgnoreCase("lt")) ~ IgnoreCase("eq").?)).! - } - - private val naryOperatorParser: Parser[String] = P(IgnoreCase("in")).! - - private val isPositiveParser: Parser[Boolean] = P(CharIn("-+").!.?).map { - case Some("-") => false - case _ => true - } - - private val digitsParser: Parser[String] = P(CharIn('0' to '9').rep(min = 1).!) // Exclude Unicode "digits" - - private val numberParser: Parser[String] = P(isPositiveParser ~ digitsParser.! ~ ("." ~ digitsParser).!.?).map { - case (false, intPart, Some(fracPart)) => s"-$intPart.${fracPart.tail}" - case (false, intPart, None) => s"-$intPart" - case (_, intPart, Some(fracPart)) => s"$intPart.${fracPart.tail}" - case (_, intPart, None) => s"$intPart" - } - - private val nAryValueParser: Parser[String] = P(CharPred(_ != ',').rep(min = 1).!) - - private val longParser: Parser[Long] = P(CharIn('0' to '9').rep(min = 1).!.map(_.toLong)) - - private val booleanParser: Parser[Boolean] = - P((IgnoreCase("true") | IgnoreCase("false")).!.map(_.toBoolean)) - - private val hexDigit: Parser[String] = P((CharIn('a' to 'f') | CharIn('A' to 'F') | CharIn('0' to '9')).!) - - private val uuidParser: Parser[UUID] = - P( - hexDigit.rep(8).! ~ "-" ~ hexDigit.rep(4).! ~ "-" ~ hexDigit.rep(4).! ~ "-" ~ hexDigit.rep(4).! ~ "-" ~ hexDigit - .rep(12) - .!).map { - case (group1, group2, group3, group4, group5) => UUID.fromString(s"$group1-$group2-$group3-$group4-$group5") - } - - private val binaryAtomParser: Parser[SearchFilterExpr.Atom.Binary] = P( - dimensionParser ~ whitespaceParser ~ - ((numericOperatorParser.! ~ whitespaceParser ~ (longParser | numberParser.!) ~ End) | - (commonOperatorParser.! ~ whitespaceParser ~ (uuidParser | booleanParser | AnyChar.rep(min = 1).!) ~ End)) - ).map { - case BinaryAtomFromTuple(atom) => atom - } - - private val nAryAtomParser: Parser[SearchFilterExpr.Atom.NAry] = P( - dimensionParser ~ whitespaceParser ~ ( - naryOperatorParser ~ whitespaceParser ~ - ((longParser.rep(min = 1, sep = ",") ~ End) | (booleanParser.rep(min = 1, sep = ",") ~ End) | - (nAryValueParser.!.rep(min = 1, sep = ",") ~ End)) - ) - ).map { - case NAryAtomFromTuple(atom) => atom - } - - private val atomParser: Parser[SearchFilterExpr.Atom] = P(binaryAtomParser | nAryAtomParser) - - @deprecated("play-akka transition", "0") - def parse(query: Map[String, Seq[String]]): Try[SearchFilterExpr] = - parse(query.toSeq.flatMap { - case (key, values) => - values.map(value => key -> value) - }) - - def parse(query: Seq[(String, String)]): Try[SearchFilterExpr] = Try { - query.toList.collect { case ("filters", value) => value } match { - case Nil => SearchFilterExpr.Empty - - case head :: Nil => - atomParser.parse(head) match { - case Parsed.Success(x, _) => x - case e: Parsed.Failure[_, _] => throw new ParseQueryArgException("filters" -> formatFailure(1, e)) - } - - case xs => - val parsed = xs.map(x => atomParser.parse(x)) - val failures: Seq[String] = parsed.zipWithIndex.collect { - case (e: Parsed.Failure[_, _], index) => formatFailure(index, e) - } - - if (failures.isEmpty) { - val filters = parsed.collect { - case Parsed.Success(x, _) => x - } - - SearchFilterExpr.Intersection.create(filters: _*) - } else { - throw new ParseQueryArgException("filters" -> failures.mkString("; ")) - } - } - } - - private def formatFailure(sectionIndex: Int, e: Parsed.Failure[_, _]): String = { - s"section $sectionIndex: ${fastparse.core.ParseError.msg(e.extra.input, e.extra.traced.expected, e.index)}" - } - -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/parsers/SortingParser.scala b/src/main/scala/xyz/driver/pdsuicommon/parsers/SortingParser.scala deleted file mode 100644 index a04d278..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/parsers/SortingParser.scala +++ /dev/null @@ -1,64 +0,0 @@ -package xyz.driver.pdsuicommon.parsers - -import xyz.driver.pdsuicommon.db.{Sorting, SortingOrder} -import fastparse.all._ -import fastparse.core.Parsed -import xyz.driver.pdsuicommon.utils.Utils._ - -import scala.util.Try - -object SortingParser { - - private val sortingOrderParser: Parser[SortingOrder] = P("-".!.?).map { - case Some(_) => SortingOrder.Descending - case None => SortingOrder.Ascending - } - - private def dimensionSortingParser(validDimensions: Seq[String]): Parser[Sorting.Dimension] = { - P(sortingOrderParser ~ StringIn(validDimensions: _*).!).map { - case (sortingOrder, field) => - val prefixedFields = field.split("\\.", 2) - prefixedFields.size match { - case 1 => Sorting.Dimension(None, toSnakeCase(field), sortingOrder) - case 2 => - Sorting.Dimension(Some(prefixedFields.head).map(toSnakeCase), - toSnakeCase(prefixedFields.last), - sortingOrder) - } - } - } - - private def sequentialSortingParser(validDimensions: Seq[String]): Parser[Sorting.Sequential] = { - P(dimensionSortingParser(validDimensions).rep(min = 1, sep = ",") ~ End).map { dimensions => - Sorting.Sequential(dimensions) - } - } - - @deprecated("play-akka transition", "0") - def parse(validDimensions: Set[String], query: Map[String, Seq[String]]): Try[Sorting] = - parse(validDimensions, query.toSeq.flatMap { - case (key, values) => - values.map(value => key -> value) - }) - - def parse(validDimensions: Set[String], query: Seq[(String, String)]): Try[Sorting] = Try { - query.toList.collect { case ("sort", value) => value } match { - case Nil => Sorting.Sequential(Seq.empty) - - case rawSorting :: Nil => - val parser = sequentialSortingParser(validDimensions.toSeq) - parser.parse(rawSorting) match { - case Parsed.Success(x, _) => x - case e: Parsed.Failure[_, _] => - throw new ParseQueryArgException("sort" -> formatFailure(e)) - } - - case _ => throw new ParseQueryArgException("sort" -> "multiple sections are not allowed") - } - } - - private def formatFailure(e: Parsed.Failure[_, _]): String = { - fastparse.core.ParseError.msg(e.extra.input, e.extra.traced.expected, e.index) - } - -} -- cgit v1.2.3