diff options
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuicommon')
4 files changed, 53 insertions, 26 deletions
diff --git a/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala b/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala index 180ebf9..3eb1a65 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala @@ -144,7 +144,9 @@ object ACL extends PhiLogging { object Hypothesis extends BaseACL( label = "hypothesis", - read = Set(TrialSummarizer, TrialAdmin) ++ TreatmentMatchingRoles + read = Set(TrialSummarizer, TrialAdmin) ++ TreatmentMatchingRoles, + create = Set(TrialAdmin), + delete = Set(TrialAdmin) ) object Criterion diff --git a/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala b/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala index 7366151..9962edf 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/db/SlickQueryBuilder.scala @@ -6,6 +6,7 @@ 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 scala.concurrent.{ExecutionContext, Future} @@ -44,6 +45,18 @@ 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) @@ -160,7 +173,7 @@ sealed trait SlickQueryBuilderParameters { def isNull(string: AnyRef) = Option(string).isEmpty || string.toString.toLowerCase == "null" def escapeDimension(dimension: SearchFilterExpr.Dimension) = { - s"$escapedTableName.$qs${dimension.name}$qs" + 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 { @@ -174,9 +187,9 @@ sealed trait SlickQueryBuilderParameters { if (conditions.nonEmpty) { val condition = conditions.head if (first) { - multipleSqlToAction(false, op, conditions.tail, condition) + multipleSqlToAction(first = false, op, conditions.tail, condition) } else { - multipleSqlToAction(false, op, conditions.tail, sql concat sql" #${op} " concat condition) + multipleSqlToAction(first = false, op, conditions.tail, sql concat sql" #${op} " concat condition) } } else sql } @@ -184,9 +197,9 @@ sealed trait SlickQueryBuilderParameters { def concatenateParameters(sql: SQLActionBuilder, first: Boolean, tail: Seq[AnyRef]): SQLActionBuilder = { if (tail.nonEmpty) { if (!first) { - concatenateParameters(sql concat sql""",${tail.head}""", false, tail.tail) + concatenateParameters(sql concat sql""",${tail.head}""", first = false, tail.tail) } else { - concatenateParameters(sql"""(${tail.head}""", false, tail.tail) + concatenateParameters(sql"""(${tail.head}""", first = false, tail.tail) } } else sql concat sql")" } @@ -196,10 +209,10 @@ sealed trait SlickQueryBuilderParameters { sql"" case AllowAll => - sql"1" + sql"1=1" case DenyAll => - sql"0" + sql"1=0" case Atom.Binary(dimension, Eq, value) if isNull(value) => sql"#${escapeDimension(dimension)} is NULL" @@ -232,17 +245,19 @@ sealed trait SlickQueryBuilderParameters { case SearchFilterNAryOperation.NotIn => sql" not in " } - val formattedValues = if (values.nonEmpty) { - concatenateParameters(sql"", true, values) - } else sql"NULL" - sql"#${escapeDimension(dimension)}" concat sqlOp concat formattedValues + 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(true, "and", filterToSqlMultiple(operands), sql"") + val filter = multipleSqlToAction(first = true, "and", filterToSqlMultiple(operands), sql"") sql"(" concat filter concat sql")" case Union(operands) => - val filter = multipleSqlToAction(true, "or", filterToSqlMultiple(operands), sql"") + val filter = multipleSqlToAction(first = true, "or", filterToSqlMultiple(operands), sql"") sql"(" concat filter concat sql")" } } diff --git a/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala b/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala index fd1856b..37e7687 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala @@ -3,6 +3,7 @@ package xyz.driver.pdsuicommon.http import akka.http.scaladsl.server._ import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.model._ +import xyz.driver.core.app.DriverApp import xyz.driver.core.rest.ContextHeaders import xyz.driver.entities.users.AuthUserInfo import xyz.driver.pdsuicommon.auth._ @@ -88,7 +89,7 @@ trait Directives { val text = errorsResponseJsonFormat.write(err).toString() HttpEntity(ContentTypes.`application/json`, text) } - RejectionHandler.default.mapRejectionResponse { + DriverApp.rejectionHandler.mapRejectionResponse { case res @ HttpResponse(_, _, ent: HttpEntity.Strict, _) => res.copy(entity = wrapContent(ent.data.utf8String)) case x => x // pass through all other types of responses diff --git a/src/main/scala/xyz/driver/pdsuicommon/parsers/SearchFilterParser.scala b/src/main/scala/xyz/driver/pdsuicommon/parsers/SearchFilterParser.scala index 85e9149..718a42d 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/parsers/SearchFilterParser.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/parsers/SearchFilterParser.scala @@ -1,5 +1,7 @@ package xyz.driver.pdsuicommon.parsers +import java.util.UUID + import xyz.driver.pdsuicommon.utils.Implicits.{toCharOps, toStringOps} import fastparse.all._ import fastparse.core.Parsed @@ -79,7 +81,7 @@ object SearchFilterParser { } private val numericOperatorParser: Parser[String] = { - P(IgnoreCase("eq") | ((IgnoreCase("gt") | IgnoreCase("lt")) ~ IgnoreCase("eq").?)).! + P(IgnoreCase("eq") | IgnoreCase("noteq") | ((IgnoreCase("gt") | IgnoreCase("lt")) ~ IgnoreCase("eq").?)).! } private val naryOperatorParser: Parser[String] = P(IgnoreCase("in")).! @@ -89,10 +91,8 @@ object SearchFilterParser { case _ => true } - // Exclude Unicode "digits" - private val digitsParser: Parser[String] = P(CharIn('0' to '9').rep(min = 1).!) + private val digitsParser: Parser[String] = P(CharIn('0' to '9').rep(min = 1).!) // Exclude Unicode "digits" - // @TODO Make complex checking here 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" @@ -107,11 +107,20 @@ object SearchFilterParser { 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 | booleanParser | numberParser.!)) | - (commonOperatorParser.! ~ whitespaceParser ~ AnyChar.rep(min = 1).!) - ) ~ End + dimensionParser ~ whitespaceParser ~ + ((numericOperatorParser.! ~ whitespaceParser ~ (longParser | numberParser.!) ~ End) | + (commonOperatorParser.! ~ whitespaceParser ~ (uuidParser | booleanParser | AnyChar.rep(min = 1).!) ~ End)) ).map { case BinaryAtomFromTuple(atom) => atom } @@ -119,9 +128,9 @@ object SearchFilterParser { private val nAryAtomParser: Parser[SearchFilterExpr.Atom.NAry] = P( dimensionParser ~ whitespaceParser ~ ( naryOperatorParser ~ whitespaceParser ~ - (longParser.rep(min = 1, sep = ",") | booleanParser.rep(min = 1, sep = ",") | nAryValueParser.!.rep(min = 1, - sep = ",")) - ) ~ End + ((longParser.rep(min = 1, sep = ",") ~ End) | (booleanParser.rep(min = 1, sep = ",") ~ End) | + (nAryValueParser.!.rep(min = 1, sep = ",") ~ End)) + ) ).map { case NAryAtomFromTuple(atom) => atom } |