From 93eb4829c0d11959709e18a7b489343550633e83 Mon Sep 17 00:00:00 2001 From: vlad Date: Thu, 13 Jul 2017 02:27:55 -0700 Subject: Updates for authentication --- .../formats/json/user/ApiPartialUser.scala | 33 +++++++++++----------- .../pdsuidomain/formats/json/user/ApiUser.scala | 19 ++++++------- 2 files changed, 25 insertions(+), 27 deletions(-) (limited to 'src/main/scala/xyz/driver/pdsuidomain') diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiPartialUser.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiPartialUser.scala index f31efb3..05395b4 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiPartialUser.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiPartialUser.scala @@ -11,9 +11,10 @@ import scala.collection._ import scala.util.Try import User._ import xyz.driver.pdsuicommon.json.JsonValidationException +import xyz.driver.pdsuicommon.json.Serialization.seqJsonFormat import xyz.driver.pdsuicommon.validation.{AdditionalConstraints, JsonValidationErrors} -final case class ApiPartialUser(email: Option[String], name: Option[String], roleId: Option[String]) { +final case class ApiPartialUser(email: Option[String], name: Option[String], roles: Option[Seq[String]]) { def applyTo(orig: User): Try[User] = Try { val validation = Map( @@ -33,9 +34,9 @@ final case class ApiPartialUser(email: Option[String], name: Option[String], rol def toDomain(id: StringId[User] = StringId(UUID.randomUUID().toString)): Try[User] = Try { val validation = Map( - JsPath \ "email" -> AdditionalConstraints.optionNonEmptyConstraint(email), - JsPath \ "name" -> AdditionalConstraints.optionNonEmptyConstraint(name), - JsPath \ "roleId" -> AdditionalConstraints.optionNonEmptyConstraint(roleId) + JsPath \ "email" -> AdditionalConstraints.optionNonEmptyConstraint(email), + JsPath \ "name" -> AdditionalConstraints.optionNonEmptyConstraint(name), + JsPath \ "roles" -> AdditionalConstraints.optionNonEmptyConstraint(roles) ) val validationErrors: JsonValidationErrors = validation.collect({ @@ -48,7 +49,7 @@ final case class ApiPartialUser(email: Option[String], name: Option[String], rol id = id, email = userEmail, name = name.get, - role = roleId.map(UserRole.roleFromString).get, + roles = roles.toSeq.flatMap(_.map(UserRole.roleFromString)).toSet, passwordHash = PasswordHash(createPassword), latestActivity = None, deleted = None @@ -63,17 +64,15 @@ object ApiPartialUser { implicit val format: Format[ApiPartialUser] = ( (JsPath \ "email").formatNullable[String](Format(Reads.email, Writes.StringWrites)) and - (JsPath \ "name").formatNullable[String](Format( - Reads.filterNot[String](ValidationError("Username is too long (max length is 255 chars)", 255))(_.length > 255), - Writes.StringWrites - )) and - (JsPath \ "roleId").formatNullable[String]( - Format(Reads - .of[String] - .filter(ValidationError("unknown role"))({ - case x if UserRole.roleFromString.isDefinedAt(x) => true - case _ => false - }), - Writes.of[String])) + (JsPath \ "name").formatNullable[String]( + Format( + Reads.filterNot[String](ValidationError("Username is too long (max length is 255 chars)", 255))( + _.length > 255), + Writes.StringWrites + )) and + (JsPath \ "roleId").formatNullable[Seq[String]]( + Format( + seqJsonFormat[String].filter(ValidationError("unknown role"))(_.forall(UserRole.roleFromString.isDefinedAt)), + Writes.of[Seq[String]])) )(ApiPartialUser.apply, unlift(ApiPartialUser.unapply)) } diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiUser.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiUser.scala index 8dbedfe..c21edd1 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiUser.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiUser.scala @@ -3,14 +3,17 @@ package xyz.driver.pdsuidomain.formats.json.user import java.time.{ZoneId, ZonedDateTime} import xyz.driver.pdsuicommon.domain.User +import xyz.driver.pdsuicommon.json.Serialization.seqJsonFormat import play.api.data.validation.ValidationError import play.api.libs.functional.syntax._ import play.api.libs.json._ +import scala.collection.Seq + final case class ApiUser(id: String, email: String, name: String, - roleId: String, + roles: Seq[String], latestActivity: Option[ZonedDateTime]) object ApiUser { @@ -19,14 +22,10 @@ object ApiUser { (JsPath \ "id").format[String] and (JsPath \ "email").format[String](Reads.email) and (JsPath \ "name").format[String] and - (JsPath \ "roleId").format[String]( - Format(Reads - .of[String] - .filter(ValidationError("unknown role"))({ - case x if UserRole.roleFromString.isDefinedAt(x) => true - case _ => false - }), - Writes.of[String])) and + (JsPath \ "roles").format( + Format( + seqJsonFormat[String].filter(ValidationError("unknown role"))(_.forall(UserRole.roleFromString.isDefinedAt)), + Writes.of[Seq[String]])) and (JsPath \ "latestActivity").formatNullable[ZonedDateTime] )(ApiUser.apply, unlift(ApiUser.unapply)) @@ -34,7 +33,7 @@ object ApiUser { user.id.id, user.email.value, user.name, - UserRole.roleToString(user.role), + user.roles.map(UserRole.roleToString).toSeq, user.latestActivity.map(ZonedDateTime.of(_, ZoneId.of("Z"))) ) } -- cgit v1.2.3 From 8353cc012328744c7cdfe0436e23d5e565bea570 Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 14 Jul 2017 10:38:59 -0700 Subject: Clean-up + New User constructor from Driver User --- build.sbt | 1 - .../scala/xyz/driver/pdsuicommon/acl/ACL.scala | 10 +- .../pdsuicommon/computation/Computation.scala | 12 +- .../scala/xyz/driver/pdsuicommon/domain/User.scala | 53 ++++++++- .../json/linkedpatient/ApiLinkedPatient.scala | 29 ----- .../services/LinkedPatientService.scala | 58 ---------- .../driver/pdsuidomain/services/MailService.scala | 39 ------- .../driver/pdsuidomain/services/UserService.scala | 127 --------------------- .../services/fake/StubMailService.scala | 13 --- .../services/rest/SendGridMailService.scala | 37 ------ 10 files changed, 66 insertions(+), 313 deletions(-) delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/formats/json/linkedpatient/ApiLinkedPatient.scala delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/services/LinkedPatientService.scala delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/services/MailService.scala delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/services/UserService.scala delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/services/fake/StubMailService.scala delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/services/rest/SendGridMailService.scala (limited to 'src/main/scala/xyz/driver/pdsuidomain') diff --git a/build.sbt b/build.sbt index c0aaf32..058bfa6 100644 --- a/build.sbt +++ b/build.sbt @@ -20,7 +20,6 @@ lazy val core = (project in file(".")) "org.asynchttpclient" % "async-http-client" % "2.0.24", "io.getquill" %% "quill-jdbc" % "1.2.1", "io.github.cloudify" %% "spdf" % "1.4.0", - "com.sendgrid" % "sendgrid-java" % "3.1.0" exclude ("org.mockito", "mockito-core"), "com.github.spullara.mustache.java" % "scala-extensions-2.11" % "0.9.4", "de.svenkubiak" % "jBCrypt" % "0.4.1", "com.google.cloud" % "google-cloud-storage" % "0.9.4-beta", diff --git a/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala b/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala index 0438dfc..276ef9f 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala @@ -243,12 +243,10 @@ object ACL extends PhiLogging { } private def check(action: String, isAllowed: AclCheck)(executorRoles: Set[Role]): Boolean = { - executorRoles.exists { role => - loggedError( - isAllowed(role), - phi"$role has no access to ${Unsafe(action)} a ${Unsafe(label)}" - ) - } + loggedError( + executorRoles.exists(isAllowed), + phi"${Unsafe(executorRoles.mkString(", "))} has no access to ${Unsafe(action)} a ${Unsafe(label)}" + ) } } } diff --git a/src/main/scala/xyz/driver/pdsuicommon/computation/Computation.scala b/src/main/scala/xyz/driver/pdsuicommon/computation/Computation.scala index 159c144..af7d051 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/computation/Computation.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/computation/Computation.scala @@ -72,6 +72,17 @@ final case class Computation[+R, +T](future: Future[Either[R, T]]) { }) } + def mapAll[R2, T2](onLeft: R => Computation[R2, T2])(onRight: T => Computation[R2, T2])( + onFailure: () => Computation[R2, T2])(implicit ec: ExecutionContext): Computation[R2, T2] = { + + Computation(future.flatMap { success => + if (success.isRight) onRight(success.right.get).future + else onLeft(success.left.get).future + } recoverWith { + case _ => onFailure().future + }) + } + def andThen(f: T => Any)(implicit ec: ExecutionContext): Computation[R, T] = map { a => f(a) a @@ -98,7 +109,6 @@ final case class Computation[+R, +T](future: Future[Either[R, T]]) { case Left(x) => x case Right(x) => x } - } object Computation { diff --git a/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala b/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala index ffc4bf9..cfc2270 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala @@ -2,7 +2,7 @@ package xyz.driver.pdsuicommon.domain import java.math.BigInteger import java.security.SecureRandom -import java.time.LocalDateTime +import java.time.{Instant, LocalDateTime, ZoneId} import xyz.driver.pdsuicommon.logging._ import xyz.driver.pdsuicommon.domain.User.Role @@ -14,7 +14,21 @@ case class User(id: StringId[User], roles: Set[Role], passwordHash: PasswordHash, latestActivity: Option[LocalDateTime], - deleted: Option[LocalDateTime]) + deleted: Option[LocalDateTime]) { + + def this(driverUser: xyz.driver.entities.users.UserInfo) { + this( + id = StringId[xyz.driver.pdsuicommon.domain.User](driverUser.id.value), + email = Email(driverUser.email.toString), + name = driverUser.name.toString, + roles = driverUser.roles.flatMap(User.mapRoles), + passwordHash = PasswordHash(""), + latestActivity = + driverUser.lastLoginTime.map(t => Instant.ofEpochMilli(t.millis).atZone(ZoneId.of("Z")).toLocalDateTime), + deleted = Option.empty[LocalDateTime] + ) + } +} object User { @@ -82,4 +96,39 @@ object User { def createPassword: String = new BigInteger(240, random).toString(32) + def mapRoles(coreRole: xyz.driver.core.auth.Role): Set[xyz.driver.pdsuicommon.domain.User.Role] = { + coreRole match { + case xyz.driver.entities.auth.AdministratorRole => + Set( + xyz.driver.pdsuicommon.domain.User.Role.SystemUser, + xyz.driver.pdsuicommon.domain.User.Role.RecordAdmin, + xyz.driver.pdsuicommon.domain.User.Role.TrialAdmin, + xyz.driver.pdsuicommon.domain.User.Role.TreatmentMatchingAdmin + ) + case xyz.driver.entities.auth.RecordAdmin => + Set(xyz.driver.pdsuicommon.domain.User.Role.RecordAdmin) + case xyz.driver.entities.auth.RecordCleaner => + Set(xyz.driver.pdsuicommon.domain.User.Role.RecordCleaner) + case xyz.driver.entities.auth.RecordOrganizer => + Set(xyz.driver.pdsuicommon.domain.User.Role.RecordOrganizer) + case xyz.driver.entities.auth.DocumentExtractor => + Set(xyz.driver.pdsuicommon.domain.User.Role.DocumentExtractor) + case xyz.driver.entities.auth.TrialSummarizer => + Set(xyz.driver.pdsuicommon.domain.User.Role.TrialSummarizer) + case xyz.driver.entities.auth.CriteriaCurator => + Set(xyz.driver.pdsuicommon.domain.User.Role.CriteriaCurator) + case xyz.driver.entities.auth.TrialAdmin => + Set(xyz.driver.pdsuicommon.domain.User.Role.TrialAdmin) + case xyz.driver.entities.auth.EligibilityVerifier => + Set(xyz.driver.pdsuicommon.domain.User.Role.EligibilityVerifier) + case xyz.driver.entities.auth.TreatmentMatchingAdmin => + Set(xyz.driver.pdsuicommon.domain.User.Role.TreatmentMatchingAdmin) + case xyz.driver.entities.auth.RoutesCurator => + Set(xyz.driver.pdsuicommon.domain.User.Role.RoutesCurator) + case xyz.driver.entities.auth.ResearchOncologist => + Set(xyz.driver.pdsuicommon.domain.User.Role.ResearchOncologist) + case _ => + Set.empty[xyz.driver.pdsuicommon.domain.User.Role] + } + } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/linkedpatient/ApiLinkedPatient.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/linkedpatient/ApiLinkedPatient.scala deleted file mode 100644 index 327bda2..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/linkedpatient/ApiLinkedPatient.scala +++ /dev/null @@ -1,29 +0,0 @@ -package xyz.driver.pdsuidomain.formats.json.linkedpatient - -import java.util.UUID - -import play.api.libs.json.{Format, Json} -import xyz.driver.pdsuicommon.domain._ -import xyz.driver.pdsuidomain.services.LinkedPatientService.RichLinkedPatient - -final case class ApiLinkedPatient(email: String, name: String, patientId: UUID, trialId: String) { - - def toDomain = RichLinkedPatient( - email = Email(email), - name = name, - patientId = UuidId(patientId), - trialId = StringId(trialId) - ) -} - -object ApiLinkedPatient { - - implicit val format: Format[ApiLinkedPatient] = Json.format[ApiLinkedPatient] - - def fromDomain(entity: RichLinkedPatient) = ApiLinkedPatient( - email = entity.email.value, - name = entity.name, - patientId = entity.patientId.id, - trialId = entity.trialId.id - ) -} diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/LinkedPatientService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/LinkedPatientService.scala deleted file mode 100644 index a69283a..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/services/LinkedPatientService.scala +++ /dev/null @@ -1,58 +0,0 @@ -package xyz.driver.pdsuidomain.services - -import xyz.driver.pdsuicommon.domain._ -import xyz.driver.pdsuicommon.error.DomainError -import xyz.driver.pdsuicommon.logging._ -import xyz.driver.pdsuidomain.entities.{LinkedPatient, Patient, Trial} - -import scala.concurrent.Future - -object LinkedPatientService { - - trait DefaultTrialNotFoundError { - def userMessage: String = "Trial not found" - } - - trait DefaultPatientNotFoundError { - def userMessage: String = "Patient not found" - } - - final case class RichLinkedPatient(email: Email, name: String, patientId: UuidId[Patient], trialId: StringId[Trial]) { - def toLinkedPatient(user: User) = LinkedPatient( - userId = user.id, - patientId = patientId, - trialId = trialId - ) - } - - object RichLinkedPatient { - implicit def toPhiString(x: RichLinkedPatient): PhiString = { - import x._ - phi"RichLinkedPatient(email=${Unsafe(email)}, patientId=$patientId, trialId=$trialId)" - } - } - - sealed trait CreateReply - object CreateReply { - type Error = CreateReply with DomainError - - /** - * @param createdUser None if a user was created before - */ - final case class Created(x: RichLinkedPatient, createdUser: Option[User]) extends CreateReply - - case object PatientNotFoundError - extends CreateReply with DefaultPatientNotFoundError with DomainError.NotFoundError - - case object TrialNotFoundError extends CreateReply with DefaultPatientNotFoundError with DomainError.NotFoundError - - final case class CommonError(userMessage: String) extends CreateReply with DomainError - } -} - -trait LinkedPatientService { - - import LinkedPatientService._ - - def create(entity: RichLinkedPatient): Future[CreateReply] -} diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/MailService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/MailService.scala deleted file mode 100644 index 53f897a..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/services/MailService.scala +++ /dev/null @@ -1,39 +0,0 @@ -package xyz.driver.pdsuidomain.services - -import java.io.{InputStream, StringReader, StringWriter} - -import xyz.driver.pdsuidomain.services.MailService.Template -import com.github.mustachejava.DefaultMustacheFactory -import com.twitter.mustache.ScalaObjectHandler - -import scala.io.Source - -object MailService { - - trait Template { - val subject: String - def parameters: Map[String, Any] - def filename: String - val contentType: String = "text/html" - - protected val factory = new DefaultMustacheFactory() - factory.setObjectHandler(new ScalaObjectHandler) - - protected def inputStream: InputStream = getClass.getClassLoader.getResourceAsStream(filename) - protected def templateContent: String = Source.fromInputStream(inputStream).getLines().mkString - - def content: String = { - val template = factory.compile(new StringReader(templateContent), filename) - val writer = new StringWriter - template - .execute(writer, parameters) - .close() - writer.toString - } - } -} - -trait MailService { - - def sendTo(email: String, template: Template): Boolean -} diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/UserService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/UserService.scala deleted file mode 100644 index b54b6a9..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/services/UserService.scala +++ /dev/null @@ -1,127 +0,0 @@ -package xyz.driver.pdsuidomain.services - -import xyz.driver.pdsuicommon.auth.{AnonymousRequestContext, AuthenticatedRequestContext} -import xyz.driver.pdsuicommon.db.{Pagination, SearchFilterExpr, Sorting} -import xyz.driver.pdsuicommon.domain._ -import xyz.driver.pdsuicommon.error.DomainError - -import scala.concurrent.Future - -object UserService { - - trait DefaultCredentialsError { - def userMessage: String = "Incorrect email/password. Try again." - } - - trait DefaultAccessDeniedError { - def userMessage: String = "Access denied" - } - - trait DefaultNotFoundError { - def userMessage: String = "User not found" - } - - sealed trait ActivateExecutorReply - object ActivateExecutorReply { - type Error = ActivateExecutorReply with DomainError - case class Entity(x: User) extends ActivateExecutorReply - case object NotFoundError extends ActivateExecutorReply with DomainError.NotFoundError { - val userMessage = "Info about you is not found on the server" - } - } - - sealed trait GetByIdReply - object GetByIdReply { - type Error = GetByIdReply with DomainError - case class Entity(x: User) extends GetByIdReply - case object AuthorizationError - extends GetByIdReply with DomainError.AuthorizationError with DefaultAccessDeniedError - case object NotFoundError extends GetByIdReply with DomainError.NotFoundError with DefaultNotFoundError - case class CommonError(userMessage: String) extends GetByIdReply with DomainError - } - - sealed trait GetByEmailReply - object GetByEmailReply { - case class Entity(x: User) extends GetByEmailReply - case object NotFoundError extends GetByEmailReply with DefaultNotFoundError with DomainError.NotFoundError { - override def userMessage: String = "Incorrect email. Try again." - } - } - - sealed trait GetByCredentialsReply - object GetByCredentialsReply { - case class Entity(x: User) extends GetByCredentialsReply - case object AuthenticationError - extends GetByCredentialsReply with DefaultCredentialsError with DomainError.AuthenticationError - case object NotFoundError extends GetByCredentialsReply with DomainError.NotFoundError with DefaultNotFoundError - } - - sealed trait GetListReply - object GetListReply { - case class EntityList(xs: Seq[User], totalFound: Int) extends GetListReply - case object AuthorizationError extends GetListReply with DomainError.AuthorizationError with DefaultNotFoundError - } - - sealed trait CreateReply - object CreateReply { - type Error = CreateReply with DomainError - case class Created(x: User) extends CreateReply - case object AuthorizationError extends CreateReply with DefaultNotFoundError with DomainError.AuthorizationError - case class UserAlreadyExistsError(email: Email) extends CreateReply with DomainError { - val userMessage = s"The user with this email already exists." - } - case class CommonError(userMessage: String) extends CreateReply with DomainError - } - - sealed trait UpdateReply - object UpdateReply { - type Error = UpdateReply with DomainError - case class Updated(updated: User) extends UpdateReply - case object AuthorizationError - extends UpdateReply with DefaultAccessDeniedError with DomainError.AuthorizationError - case class CommonError(userMessage: String) extends UpdateReply with DomainError - } - - sealed trait DeleteReply - object DeleteReply { - type Error = DeleteReply with DomainError - case object Deleted extends DeleteReply - case class AuthorizationError(user: User) - extends DeleteReply with DefaultAccessDeniedError with DomainError.AuthorizationError - case object AssignedToRecordAndDocumentError extends DeleteReply with DomainError { - val userMessage = "User is can not be deleted because he has record and document in work" - } - case object NotFoundError extends DeleteReply with DefaultNotFoundError with DomainError.NotFoundError - case class CommonError(userMessage: String) extends DeleteReply with DomainError - } -} - -trait UserService { - - import UserService._ - - /** - * Utility method for getting request executor. - */ - def activateExecutor(executorId: StringId[User])( - implicit requestContext: AnonymousRequestContext): Future[ActivateExecutorReply] - - def getById(userId: StringId[User])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] - - def getByEmail(email: Email)(implicit requestContext: AnonymousRequestContext): Future[GetByEmailReply] - - def getByCredentials(email: Email, password: String)( - implicit requestContext: AnonymousRequestContext): Future[GetByCredentialsReply] - - def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty, - sorting: Option[Sorting] = None, - pagination: Option[Pagination] = None)( - implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] - - def create(draftUser: User)(implicit requestContext: AuthenticatedRequestContext): Future[CreateReply] - - def update(origUser: User, draftUser: User)( - implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] - - def delete(userId: StringId[User])(implicit requestContext: AuthenticatedRequestContext): Future[DeleteReply] -} diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/fake/StubMailService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/fake/StubMailService.scala deleted file mode 100644 index 932da67..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/services/fake/StubMailService.scala +++ /dev/null @@ -1,13 +0,0 @@ -package xyz.driver.pdsuidomain.services.fake - -import xyz.driver.pdsuicommon.logging._ -import xyz.driver.pdsuidomain.services.MailService -import xyz.driver.pdsuidomain.services.MailService.Template - -object StubMailService extends MailService with PhiLogging { - - override def sendTo(email: String, template: Template): Boolean = { - logger.debug(phi"sendTo(email=${Unsafe(email)}") - true - } -} diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/SendGridMailService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/SendGridMailService.scala deleted file mode 100644 index bb3228e..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/SendGridMailService.scala +++ /dev/null @@ -1,37 +0,0 @@ -package xyz.driver.pdsuidomain.services.rest - -import com.sendgrid._ -import xyz.driver.pdsuicommon.logging._ -import xyz.driver.pdsuidomain.services.MailService -import xyz.driver.pdsuidomain.services.MailService.Template - -import scala.util.control.NonFatal - -class SendGridMailService(apiKey: String, from: String) extends MailService with PhiLogging { - private val ExpectedHttpCode = 202 - - def sendTo(email: String, template: Template): Boolean = { - val to = new Email(email) - val content = new Content(template.contentType, template.content) - val mail = new Mail(new Email(from), template.subject, to, content) - - val request = new Request() - val sendGrid = new SendGrid(apiKey) - - try { - request.method = Method.POST - request.endpoint = "mail/send" - request.body = mail.build() - val response = sendGrid.api(request) - if (response.statusCode != ExpectedHttpCode) { - logger.error(phi"Unexpected response: ${Unsafe(response.statusCode)}, ${Unsafe(response.body.take(100))}") - } - - response.statusCode == ExpectedHttpCode - } catch { - case NonFatal(e) => - logger.error(phi"Can not send an email: $e") - false - } - } -} -- cgit v1.2.3 From 700faea527700d399bf56a7b4e96b6a56ec4d8fa Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 14 Jul 2017 11:07:19 -0700 Subject: Ability to map roles back to Driver's roles --- .../scala/xyz/driver/pdsuicommon/domain/User.scala | 31 ++++++++++++++++++++++ .../pdsuidomain/entities/LinkedPatient.scala | 14 ---------- 2 files changed, 31 insertions(+), 14 deletions(-) delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/entities/LinkedPatient.scala (limited to 'src/main/scala/xyz/driver/pdsuidomain') diff --git a/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala b/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala index cfc2270..63eef85 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala @@ -131,4 +131,35 @@ object User { Set.empty[xyz.driver.pdsuicommon.domain.User.Role] } } + + def mapRolesToDriver(pdsuiRole: xyz.driver.pdsuicommon.domain.User.Role): Set[xyz.driver.core.auth.Role] = { + pdsuiRole match { + case xyz.driver.pdsuicommon.domain.User.Role.SystemUser => + Set(xyz.driver.entities.auth.AdministratorRole) + case xyz.driver.pdsuicommon.domain.User.Role.RecordAdmin => + Set(xyz.driver.entities.auth.RecordAdmin) + case xyz.driver.pdsuicommon.domain.User.Role.RecordCleaner => + Set(xyz.driver.entities.auth.RecordCleaner) + case xyz.driver.pdsuicommon.domain.User.Role.RecordOrganizer => + Set(xyz.driver.entities.auth.RecordOrganizer) + case xyz.driver.pdsuicommon.domain.User.Role.DocumentExtractor => + Set(xyz.driver.entities.auth.DocumentExtractor) + case xyz.driver.pdsuicommon.domain.User.Role.TrialSummarizer => + Set(xyz.driver.entities.auth.TrialSummarizer) + case xyz.driver.pdsuicommon.domain.User.Role.CriteriaCurator => + Set(xyz.driver.entities.auth.CriteriaCurator) + case xyz.driver.pdsuicommon.domain.User.Role.TrialAdmin => + Set(xyz.driver.entities.auth.TrialAdmin) + case xyz.driver.pdsuicommon.domain.User.Role.EligibilityVerifier => + Set(xyz.driver.entities.auth.EligibilityVerifier) + case xyz.driver.pdsuicommon.domain.User.Role.TreatmentMatchingAdmin => + Set(xyz.driver.entities.auth.TreatmentMatchingAdmin) + case xyz.driver.pdsuicommon.domain.User.Role.RoutesCurator => + Set(xyz.driver.entities.auth.RoutesCurator) + case xyz.driver.pdsuicommon.domain.User.Role.ResearchOncologist => + Set(xyz.driver.entities.auth.ResearchOncologist) + case _ => + Set.empty[xyz.driver.core.auth.Role] + } + } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/LinkedPatient.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/LinkedPatient.scala deleted file mode 100644 index 28f279f..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/entities/LinkedPatient.scala +++ /dev/null @@ -1,14 +0,0 @@ -package xyz.driver.pdsuidomain.entities - -import xyz.driver.pdsuicommon.domain._ -import xyz.driver.pdsuicommon.logging._ - -object LinkedPatient { - - implicit def toPhiString(x: LinkedPatient): PhiString = { - import x._ - phi"LinkedPatient(userId=$userId, patientId=$patientId, trialId=$trialId)" - } -} - -case class LinkedPatient(userId: StringId[User], patientId: UuidId[Patient], trialId: StringId[Trial]) -- cgit v1.2.3 From 01ead151be45774ed3fc08ef010215a5f5728498 Mon Sep 17 00:00:00 2001 From: vlad Date: Mon, 17 Jul 2017 19:35:53 -0700 Subject: Removing PasswordHash and jbCrypt lib from dependencies --- build.sbt | 1 - .../pdsuicommon/computation/Computation.scala | 5 +-- .../driver/pdsuicommon/domain/PasswordHash.scala | 36 ---------------------- .../scala/xyz/driver/pdsuicommon/domain/User.scala | 2 -- .../driver/pdsuicommon/json/Serialization.scala | 6 ---- .../formats/json/user/ApiPartialUser.scala | 2 -- .../scala/xyz/driver/pdsuicommon/BaseSuite.scala | 1 - .../xyz/driver/pdsuicommon/utils/DiffUtils.scala | 9 ------ 8 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 src/main/scala/xyz/driver/pdsuicommon/domain/PasswordHash.scala (limited to 'src/main/scala/xyz/driver/pdsuidomain') diff --git a/build.sbt b/build.sbt index 058bfa6..4f3f229 100644 --- a/build.sbt +++ b/build.sbt @@ -21,7 +21,6 @@ lazy val core = (project in file(".")) "io.getquill" %% "quill-jdbc" % "1.2.1", "io.github.cloudify" %% "spdf" % "1.4.0", "com.github.spullara.mustache.java" % "scala-extensions-2.11" % "0.9.4", - "de.svenkubiak" % "jBCrypt" % "0.4.1", "com.google.cloud" % "google-cloud-storage" % "0.9.4-beta", "com.github.pureconfig" %% "pureconfig" % "0.7.2", "ai.x" %% "diff" % "1.2.0-get-simple-name-fix" % "test", diff --git a/src/main/scala/xyz/driver/pdsuicommon/computation/Computation.scala b/src/main/scala/xyz/driver/pdsuicommon/computation/Computation.scala index af7d051..a9430e3 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/computation/Computation.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/computation/Computation.scala @@ -75,10 +75,7 @@ final case class Computation[+R, +T](future: Future[Either[R, T]]) { def mapAll[R2, T2](onLeft: R => Computation[R2, T2])(onRight: T => Computation[R2, T2])( onFailure: () => Computation[R2, T2])(implicit ec: ExecutionContext): Computation[R2, T2] = { - Computation(future.flatMap { success => - if (success.isRight) onRight(success.right.get).future - else onLeft(success.left.get).future - } recoverWith { + Computation(future.flatMap(_.fold(onLeft, onRight).future).recoverWith { case _ => onFailure().future }) } diff --git a/src/main/scala/xyz/driver/pdsuicommon/domain/PasswordHash.scala b/src/main/scala/xyz/driver/pdsuicommon/domain/PasswordHash.scala deleted file mode 100644 index 337d925..0000000 --- a/src/main/scala/xyz/driver/pdsuicommon/domain/PasswordHash.scala +++ /dev/null @@ -1,36 +0,0 @@ -package xyz.driver.pdsuicommon.domain - -import java.nio.charset.Charset - -import org.mindrot.jbcrypt.BCrypt - -final case class PasswordHash(value: Array[Byte]) { - - lazy val hashString: String = new String(value, Charset.forName("UTF-8")) - - override def toString: String = { - s"${this.getClass.getSimpleName}($hashString)" - } - - override def equals(that: Any): Boolean = { - that match { - case thatHash: PasswordHash => java.util.Arrays.equals(this.value, thatHash.value) - case _ => false - } - } - - override def hashCode(): Int = - 42 + java.util.Arrays.hashCode(this.value) - - def is(password: String): Boolean = - BCrypt.checkpw(password, hashString) -} - -object PasswordHash { - - def apply(password: String): PasswordHash = - new PasswordHash(getHash(password)) - - private def getHash(str: String): Array[Byte] = - BCrypt.hashpw(str, BCrypt.gensalt()).getBytes(Charset.forName("UTF-8")) -} diff --git a/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala b/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala index 63eef85..b400a71 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/domain/User.scala @@ -12,7 +12,6 @@ case class User(id: StringId[User], email: Email, name: String, roles: Set[Role], - passwordHash: PasswordHash, latestActivity: Option[LocalDateTime], deleted: Option[LocalDateTime]) { @@ -22,7 +21,6 @@ case class User(id: StringId[User], email = Email(driverUser.email.toString), name = driverUser.name.toString, roles = driverUser.roles.flatMap(User.mapRoles), - passwordHash = PasswordHash(""), latestActivity = driverUser.lastLoginTime.map(t => Instant.ofEpochMilli(t.millis).atZone(ZoneId.of("Z")).toLocalDateTime), deleted = Option.empty[LocalDateTime] diff --git a/src/main/scala/xyz/driver/pdsuicommon/json/Serialization.scala b/src/main/scala/xyz/driver/pdsuicommon/json/Serialization.scala index 9800903..8231ddb 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/json/Serialization.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/json/Serialization.scala @@ -40,12 +40,6 @@ object Serialization { private val emailJsonWrites: Writes[Email] = Writes(email => JsString(email.value)) implicit val emailJsonFormat: Format[Email] = Format(emailJsonReads, emailJsonWrites) - private val passwordHashJsonReads: Reads[PasswordHash] = - Reads.StringReads.map(hash => PasswordHash(hash.getBytes("UTF-8"))) - private val passwordHashJsonWrites: Writes[PasswordHash] = Writes( - passwordHash => JsString(passwordHash.value.toString)) - implicit val passwordHashJsonFormat: Format[PasswordHash] = Format(passwordHashJsonReads, passwordHashJsonWrites) - 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/pdsuidomain/formats/json/user/ApiPartialUser.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiPartialUser.scala index 05395b4..2f476e7 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiPartialUser.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/user/ApiPartialUser.scala @@ -9,7 +9,6 @@ import play.api.libs.json._ import scala.collection._ import scala.util.Try -import User._ import xyz.driver.pdsuicommon.json.JsonValidationException import xyz.driver.pdsuicommon.json.Serialization.seqJsonFormat import xyz.driver.pdsuicommon.validation.{AdditionalConstraints, JsonValidationErrors} @@ -50,7 +49,6 @@ final case class ApiPartialUser(email: Option[String], name: Option[String], rol email = userEmail, name = name.get, roles = roles.toSeq.flatMap(_.map(UserRole.roleFromString)).toSet, - passwordHash = PasswordHash(createPassword), latestActivity = None, deleted = None ) diff --git a/src/test/scala/xyz/driver/pdsuicommon/BaseSuite.scala b/src/test/scala/xyz/driver/pdsuicommon/BaseSuite.scala index 54c46c3..7c9d8c4 100644 --- a/src/test/scala/xyz/driver/pdsuicommon/BaseSuite.scala +++ b/src/test/scala/xyz/driver/pdsuicommon/BaseSuite.scala @@ -20,7 +20,6 @@ trait BaseSuite extends FreeSpecLike with DiffUtils with ScalaFutures { email = Email(email), name = "Test", roles = Set(role), - passwordHash = PasswordHash(password), latestActivity = Some(LocalDateTime.now(ZoneId.of("Z"))), deleted = None ) diff --git a/src/test/scala/xyz/driver/pdsuicommon/utils/DiffUtils.scala b/src/test/scala/xyz/driver/pdsuicommon/utils/DiffUtils.scala index cc2ef0a..156cb08 100644 --- a/src/test/scala/xyz/driver/pdsuicommon/utils/DiffUtils.scala +++ b/src/test/scala/xyz/driver/pdsuicommon/utils/DiffUtils.scala @@ -5,7 +5,6 @@ import java.time.{LocalDate, LocalDateTime} import ai.x.diff._ import org.scalatest.Assertions -import xyz.driver.pdsuicommon.domain.PasswordHash import xyz.driver.pdsuidomain.entities.{Document, ExtractedData, MedicalRecord} import scala.io.AnsiColor @@ -41,14 +40,6 @@ trait DiffUtils { this: Assertions => } } - implicit def passwordHashDiffShow: DiffShow[PasswordHash] = new DiffShow[PasswordHash] { - def show(x: PasswordHash): String = s"PasswordHash($x)" - def diff(left: PasswordHash, right: PasswordHash): Comparison = { - if (left.equals(right)) Identical(show(left)) - else Different(showChange(left, right)) - } - } - implicit def metaDiffShow: DiffShow[MedicalRecord.Meta] = new DiffShow[MedicalRecord.Meta] { def show(x: MedicalRecord.Meta): String = s"MedicalRecord.Meta($x)" def diff(left: MedicalRecord.Meta, right: MedicalRecord.Meta): Comparison = { -- cgit v1.2.3 From 4c9d4f0679f7b1f4ab179c8d3208cf207c81635a Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 21 Jul 2017 15:11:19 -0700 Subject: Export service separation --- .../scala/xyz/driver/pdsuicommon/acl/ACL.scala | 33 ++---------- .../entities/export/trial/ExportTrial.scala | 27 ---------- .../formats/json/export/ApiExportTrial.scala | 26 --------- .../formats/json/export/ApiExportTrialArm.scala | 9 +++- .../json/export/ApiExportTrialLabelCriterion.scala | 18 ++++++- .../formats/json/export/ApiExportTrialList.scala | 15 ------ .../json/export/ApiExportTrialWithLabels.scala | 19 ++++++- .../pdsuidomain/formats/json/trial/ApiTrial.scala | 2 +- .../pdsuidomain/services/ExportService.scala | 63 ---------------------- .../services/MedicalRecordService.scala | 5 +- .../pdsuidomain/services/PatientService.scala | 15 ++++++ .../driver/pdsuidomain/services/TrialService.scala | 18 +++++++ .../services/rest/RestTrialService.scala | 15 ++++-- 13 files changed, 94 insertions(+), 171 deletions(-) delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrial.scala delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrial.scala delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialList.scala delete mode 100644 src/main/scala/xyz/driver/pdsuidomain/services/ExportService.scala (limited to 'src/main/scala/xyz/driver/pdsuidomain') diff --git a/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala b/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala index 276ef9f..48a86e3 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala @@ -19,21 +19,6 @@ object ACL extends PhiLogging { // Common - object User - extends BaseACL( - label = "user", - create = Set(RecordAdmin, TrialAdmin, TreatmentMatchingAdmin), - read = Allow, - update = Allow, - delete = Set(RecordAdmin, TrialAdmin, TreatmentMatchingAdmin) - ) - - object Label - extends BaseACL( - label = "label", - read = RepRoles ++ TcRoles ++ TreatmentMatchingRoles + ResearchOncologist - ) - object UserHistory extends BaseACL( label = "user history", @@ -53,7 +38,7 @@ object ACL extends PhiLogging { object MedicalRecord extends BaseACL( label = "medical record", - read = RepRoles + RoutesCurator + TreatmentMatchingAdmin + ResearchOncologist, + read = RepRoles + RoutesCurator + TreatmentMatchingAdmin + ResearchOncologist + SystemUser, update = RepRoles - DocumentExtractor ) @@ -75,12 +60,6 @@ object ACL extends PhiLogging { delete = Set(DocumentExtractor, RecordAdmin) ) - object Keyword - extends BaseACL( - label = "keyword", - read = Set(DocumentExtractor, RecordAdmin) - ) - object ProviderType extends BaseACL( label = "provider type", @@ -107,7 +86,7 @@ object ACL extends PhiLogging { object Trial extends BaseACL( label = "trial", - read = TcRoles + RoutesCurator + TreatmentMatchingAdmin + ResearchOncologist, + read = TcRoles + RoutesCurator + TreatmentMatchingAdmin + ResearchOncologist + SystemUser, update = TcRoles ) @@ -156,12 +135,6 @@ object ACL extends PhiLogging { delete = Set(TrialSummarizer, TrialAdmin) ) - object Category - extends BaseACL( - label = "category", - read = Set(DocumentExtractor, RecordAdmin, CriteriaCurator, TrialAdmin) - ) - object Intervention extends BaseACL( label = "intervention", @@ -180,7 +153,7 @@ object ACL extends PhiLogging { object Patient extends BaseACL( label = "patient", - read = TreatmentMatchingRoles + ResearchOncologist, + read = TreatmentMatchingRoles + ResearchOncologist + SystemUser, update = TreatmentMatchingRoles ) diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrial.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrial.scala deleted file mode 100644 index 1aed121..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/entities/export/trial/ExportTrial.scala +++ /dev/null @@ -1,27 +0,0 @@ -package xyz.driver.pdsuidomain.entities.export.trial - -import java.time.LocalDateTime - -import xyz.driver.pdsuicommon.domain._ -import xyz.driver.pdsuicommon.logging._ -import xyz.driver.pdsuidomain.entities.Trial - -final case class ExportTrial(nctId: StringId[Trial], - trialId: UuidId[Trial], - condition: Trial.Condition, - lastReviewed: LocalDateTime) - -object ExportTrial { - - implicit def toPhiString(x: ExportTrial): PhiString = { - import x._ - phi"ExportTrial(nctId=$nctId, trialId=$trialId, condition=${Unsafe(condition)}, lastReviewed=$lastReviewed)" - } - - def fromDomain(x: Trial) = ExportTrial( - nctId = x.id, - trialId = x.externalId, - condition = x.condition, - lastReviewed = x.lastUpdate - ) -} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrial.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrial.scala deleted file mode 100644 index 0c9cb71..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrial.scala +++ /dev/null @@ -1,26 +0,0 @@ -package xyz.driver.pdsuidomain.formats.json.export - -import java.time.ZoneId - -import play.api.libs.functional.syntax._ -import play.api.libs.json.{Format, JsPath} -import xyz.driver.pdsuidomain.entities.export.trial.ExportTrial - -final case class ApiExportTrial(nctId: String, trialId: String, disease: String, lastReviewed: Long) - -object ApiExportTrial { - - implicit val format: Format[ApiExportTrial] = ( - (JsPath \ "nctId").format[String] and - (JsPath \ "trialId").format[String] and - (JsPath \ "disease").format[String] and - (JsPath \ "lastReviewed").format[Long] - )(ApiExportTrial.apply, unlift(ApiExportTrial.unapply)) - - def fromDomain(trial: ExportTrial): ApiExportTrial = ApiExportTrial( - nctId = trial.nctId.id, - trialId = trial.trialId.toString, - disease = trial.condition.toString.toUpperCase, - lastReviewed = trial.lastReviewed.atZone(ZoneId.of("Z")).toEpochSecond - ) -} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialArm.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialArm.scala index b2b02eb..ea96f58 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialArm.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialArm.scala @@ -2,9 +2,16 @@ package xyz.driver.pdsuidomain.formats.json.export import play.api.libs.functional.syntax._ import play.api.libs.json._ +import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuidomain.entities.Arm import xyz.driver.pdsuidomain.entities.export.trial.ExportTrialArm -final case class ApiExportTrialArm(armId: String, armName: String) +final case class ApiExportTrialArm(armId: String, armName: String) { + + def toDomain: ExportTrialArm = { + ExportTrialArm(LongId[Arm](armId.toLong), armName) + } +} object ApiExportTrialArm { diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialLabelCriterion.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialLabelCriterion.scala index 2564efe..df1ebb4 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialLabelCriterion.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialLabelCriterion.scala @@ -1,9 +1,10 @@ package xyz.driver.pdsuidomain.formats.json.export -import xyz.driver.pdsuicommon.domain.FuzzyValue import play.api.libs.functional.syntax._ import play.api.libs.json._ +import xyz.driver.pdsuicommon.domain.{FuzzyValue, LongId} import xyz.driver.pdsuidomain.entities.export.trial.ExportTrialLabelCriterion +import xyz.driver.pdsuidomain.entities.{Arm, Criterion, Label} final case class ApiExportTrialLabelCriterion(value: String, labelId: String, @@ -11,7 +12,20 @@ final case class ApiExportTrialLabelCriterion(value: String, criterionText: String, armIds: List[String], isCompound: Boolean, - isDefining: Boolean) + isDefining: Boolean) { + + def toDomain: ExportTrialLabelCriterion = { + ExportTrialLabelCriterion( + LongId[Criterion](criterionId.toLong), + FuzzyValue.fromString.lift(value).map(_ == FuzzyValue.Yes), + LongId[Label](labelId.toLong), + armIds.map(armId => LongId[Arm](armId.toLong)).toSet, + criterionText, + isCompound, + isDefining + ) + } +} object ApiExportTrialLabelCriterion { diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialList.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialList.scala deleted file mode 100644 index 93ee42e..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialList.scala +++ /dev/null @@ -1,15 +0,0 @@ -package xyz.driver.pdsuidomain.formats.json.export - -import play.api.libs.json.{Format, Json} -import xyz.driver.pdsuidomain.entities.export.trial.ExportTrial - -final case class ApiExportTrialList(trials: Seq[ApiExportTrial]) - -object ApiExportTrialList { - - implicit val format: Format[ApiExportTrialList] = Json.format - - def fromDomain(trialList: Seq[ExportTrial]) = ApiExportTrialList( - trials = trialList.map(ApiExportTrial.fromDomain) - ) -} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialWithLabels.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialWithLabels.scala index de76ac0..e383a1f 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialWithLabels.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/export/ApiExportTrialWithLabels.scala @@ -1,9 +1,11 @@ package xyz.driver.pdsuidomain.formats.json.export -import java.time.ZoneId +import java.time.{Instant, ZoneId} import play.api.libs.functional.syntax._ import play.api.libs.json.{Format, JsPath} +import xyz.driver.pdsuicommon.domain.{StringId, UuidId} +import xyz.driver.pdsuidomain.entities.Trial import xyz.driver.pdsuidomain.entities.export.trial.ExportTrialWithLabels final case class ApiExportTrialWithLabels(nctId: String, @@ -12,7 +14,20 @@ final case class ApiExportTrialWithLabels(nctId: String, lastReviewed: Long, labelVersion: Long, arms: List[ApiExportTrialArm], - criteria: List[ApiExportTrialLabelCriterion]) + criteria: List[ApiExportTrialLabelCriterion]) { + + def toDomain: ExportTrialWithLabels = { + ExportTrialWithLabels( + StringId[Trial](nctId), + UuidId[Trial](trialId), + condition, + lastReviewed = Instant.ofEpochMilli(lastReviewed).atZone(ZoneId.of("Z")).toLocalDateTime, + labelVersion, + arms.map(_.toDomain), + criteria.map(_.toDomain) + ) + } +} object ApiExportTrialWithLabels { diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala index f59836e..51bd03f 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trial/ApiTrial.scala @@ -36,7 +36,7 @@ final case class ApiTrial(id: String, previousStatus = this.previousStatus.map(s => TrialStatus.statusFromString(s)), previousAssignee = this.previousAssignee.map(id => StringId(id)), lastActiveUserId = this.lastActiveUser.map(id => StringId(id)), - lastUpdate = this.lastUpdate.toLocalDateTime(), + lastUpdate = this.lastUpdate.toLocalDateTime, condition = Trial.Condition .fromString(this.condition) .getOrElse( diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/ExportService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/ExportService.scala deleted file mode 100644 index a313703..0000000 --- a/src/main/scala/xyz/driver/pdsuidomain/services/ExportService.scala +++ /dev/null @@ -1,63 +0,0 @@ -package xyz.driver.pdsuidomain.services - -import java.time.LocalDateTime - -import xyz.driver.pdsuicommon.auth.AnonymousRequestContext -import xyz.driver.pdsuicommon.db.SearchFilterExpr -import xyz.driver.pdsuicommon.domain._ -import xyz.driver.pdsuicommon.error.DomainError -import xyz.driver.pdsuidomain.entities.export.patient.ExportPatientWithLabels -import xyz.driver.pdsuidomain.entities.export.trial.{ExportTrial, ExportTrialWithLabels} -import xyz.driver.pdsuidomain.entities.{MedicalRecord, Patient, Trial} - -import scala.concurrent.Future - -object ExportService { - - sealed trait GetPatientReply - object GetPatientReply { - type Error = GetPatientReply with DomainError - - final case class Entity(x: ExportPatientWithLabels) extends GetPatientReply - - case object NotFoundError extends GetPatientReply with DomainError.NotFoundError { - def userMessage: String = "Patient not found" - } - } - - sealed trait GetTrialListReply - object GetTrialListReply { - final case class EntityList(xs: Seq[ExportTrial], totalFound: Int, lastUpdate: Option[LocalDateTime]) - extends GetTrialListReply - } - - sealed trait GetTrialReply - object GetTrialReply { - type Error = GetTrialReply with DomainError - - final case class Entity(x: ExportTrialWithLabels) extends GetTrialReply - - case object NotFoundError extends GetTrialReply with DomainError.NotFoundError { - def userMessage: String = "Trial not found" - } - } -} - -trait ExportService { - - import ExportService._ - - def getPatient(id: UuidId[Patient])(implicit requestContext: AnonymousRequestContext): Future[GetPatientReply] - - def getTrialList(filter: SearchFilterExpr = SearchFilterExpr.Empty)( - implicit requestContext: AnonymousRequestContext): Future[GetTrialListReply] - - def getTrial(trialId: StringId[Trial], condition: String)( - implicit requestContext: AnonymousRequestContext): Future[GetTrialReply] - - def getRecords(patientId: UuidId[Patient])( - implicit requestContext: AnonymousRequestContext): Future[MedicalRecordService.GetListReply] - - def getRecordPdf(recordId: LongId[MedicalRecord])( - implicit requestContext: AnonymousRequestContext): Future[MedicalRecordService.GetPdfSourceReply] -} diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/MedicalRecordService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/MedicalRecordService.scala index b2fc9f9..cfdb2b1 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/MedicalRecordService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/MedicalRecordService.scala @@ -4,7 +4,7 @@ import java.time.LocalDateTime import xyz.driver.pdsuicommon.auth.AuthenticatedRequestContext import xyz.driver.pdsuicommon.db._ -import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuicommon.domain.{LongId, UuidId} import xyz.driver.pdsuicommon.error._ import xyz.driver.pdsuidomain.entities.MedicalRecord.PdfSource import xyz.driver.pdsuidomain.entities._ @@ -93,6 +93,9 @@ trait MedicalRecordService { def getById(recordId: LongId[MedicalRecord])( implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] + def getPatientRecords(patientId: UuidId[Patient])( + implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] + def getPdfSource(recordId: LongId[MedicalRecord])( implicit requestContext: AuthenticatedRequestContext): Future[GetPdfSourceReply] diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala index ce70934..b2a140c 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala @@ -8,6 +8,7 @@ import xyz.driver.pdsuicommon.domain._ import xyz.driver.pdsuicommon.error.DomainError import xyz.driver.pdsuicommon.logging._ import xyz.driver.pdsuidomain.entities._ +import xyz.driver.pdsuidomain.entities.export.patient.ExportPatientWithLabels import scala.concurrent.Future @@ -50,6 +51,17 @@ object PatientService { } } + sealed trait GetPatientWithLabelsReply + object GetPatientWithLabelsReply { + type Error = GetPatientWithLabelsReply with DomainError + + final case class Entity(x: ExportPatientWithLabels) extends GetPatientWithLabelsReply + + case object NotFoundError extends GetPatientWithLabelsReply with DomainError.NotFoundError { + def userMessage: String = "Patient not found" + } + } + sealed trait UpdateReply object UpdateReply { type Error = UpdateReply with DomainError @@ -76,6 +88,9 @@ trait PatientService { def getById(id: UuidId[Patient])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] + def getExportPatient(id: UuidId[Patient])( + implicit requestContext: AuthenticatedRequestContext): Future[GetPatientWithLabelsReply] + def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty, sorting: Option[Sorting] = None, pagination: Option[Pagination] = None)( diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/TrialService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/TrialService.scala index 3148842..d140d27 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/TrialService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/TrialService.scala @@ -9,6 +9,7 @@ import xyz.driver.pdsuicommon.error.DomainError import xyz.driver.pdsuicommon.logging._ import xyz.driver.pdsuidomain.entities.Trial import xyz.driver.pdsuidomain.entities.Trial.PdfSource +import xyz.driver.pdsuidomain.entities.export.trial.ExportTrialWithLabels import scala.concurrent.Future @@ -51,6 +52,20 @@ object TrialService { } } + sealed trait GetTrialWithLabelsReply + object GetTrialWithLabelsReply { + type Error = GetTrialWithLabelsReply with DomainError + + final case class Entity(x: ExportTrialWithLabels) extends GetTrialWithLabelsReply + + case object NotFoundError extends GetTrialWithLabelsReply with DomainError.NotFoundError { + def userMessage: String = "Trial not found" + } + + case object AuthorizationError + extends GetTrialWithLabelsReply with DomainError.AuthorizationError with DefaultAccessDeniedError + } + sealed trait GetPdfSourceReply object GetPdfSourceReply { type Error = GetPdfSourceReply with DomainError @@ -95,6 +110,9 @@ trait TrialService { def getById(id: StringId[Trial])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] + def getTrialWithLabels(trialId: StringId[Trial], condition: String)( + implicit requestContext: AuthenticatedRequestContext): Future[GetTrialWithLabelsReply] + def getPdfSource(trialId: StringId[Trial])( implicit requestContext: AuthenticatedRequestContext): Future[GetPdfSourceReply] diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala index e1f9d87..b8aae88 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala @@ -1,8 +1,6 @@ package xyz.driver.pdsuidomain.services.rest -import scala.NotImplementedError import scala.concurrent.{ExecutionContext, Future} - import akka.http.scaladsl.marshalling.Marshal import akka.http.scaladsl.model._ import akka.stream.ActorMaterializer @@ -12,6 +10,7 @@ import xyz.driver.pdsuicommon.db._ import xyz.driver.pdsuicommon.domain._ import xyz.driver.pdsuidomain.entities._ import xyz.driver.pdsuidomain.formats.json.ListResponse +import xyz.driver.pdsuidomain.formats.json.export.ApiExportTrialWithLabels import xyz.driver.pdsuidomain.formats.json.trial.ApiTrial import xyz.driver.pdsuidomain.services.TrialService @@ -33,6 +32,17 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)( } } + def getTrialWithLabels(trialId: StringId[Trial], condition: String)( + implicit requestContext: AuthenticatedRequestContext): Future[GetTrialWithLabelsReply] = { + val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/export/trial/$trialId")) + for { + response <- transport.sendRequestGetResponse(requestContext)(request) + reply <- apiResponse[ApiExportTrialWithLabels](response) + } yield { + GetTrialWithLabelsReply.Entity(reply.toDomain) + } + } + def getPdfSource(trialId: StringId[Trial])( implicit requestContext: AuthenticatedRequestContext): Future[GetPdfSourceReply] = Future.failed(new NotImplementedError("Streaming PDF over network is not supported.")) @@ -91,5 +101,4 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)( singleAction(origTrial, "archive") def unassign(origTrial: Trial)(implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] = singleAction(origTrial, "unassign") - } -- cgit v1.2.3 From 1ba5873c74514fd13efbc9e8ea5bafebed3a03dc Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 21 Jul 2017 16:15:29 -0700 Subject: Implementing new method in FakeTrialService --- .../services/fake/FakeTrialService.scala | 33 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'src/main/scala/xyz/driver/pdsuidomain') diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala index bd176b3..7c0e313 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/fake/FakeTrialService.scala @@ -1,11 +1,15 @@ package xyz.driver.pdsuidomain.services.fake import java.time.LocalDateTime + +import xyz.driver.core.generators import xyz.driver.pdsuicommon.auth.AuthenticatedRequestContext import xyz.driver.pdsuicommon.db._ -import xyz.driver.pdsuicommon.domain.{StringId, UuidId} -import xyz.driver.pdsuidomain.entities.Trial +import xyz.driver.pdsuicommon.domain.{LongId, StringId, UuidId} +import xyz.driver.pdsuidomain.entities.{Arm, Criterion, Label, Trial} +import xyz.driver.pdsuidomain.entities.export.trial.{ExportTrialArm, ExportTrialLabelCriterion, ExportTrialWithLabels} import xyz.driver.pdsuidomain.services.TrialService + import scala.concurrent.Future class FakeTrialService extends TrialService { @@ -49,6 +53,31 @@ class FakeTrialService extends TrialService { implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = Future.successful(GetListReply.EntityList(Seq(trial), 1, None)) + override def getTrialWithLabels(trialId: StringId[Trial], condition: String)( + implicit requestContext: AuthenticatedRequestContext): Future[GetTrialWithLabelsReply] = { + Future.successful( + GetTrialWithLabelsReply.Entity(ExportTrialWithLabels( + StringId[Trial]("NCT" + generators.nextInt(999999).toString), + UuidId[Trial](generators.nextUuid()), + generators.oneOf("adenocarcinoma", "breast", "prostate"), + LocalDateTime.now(), + labelVersion = 1L, + generators.listOf(new ExportTrialArm( + LongId[Arm](generators.nextInt(999999).toLong), + generators.nextName().value + )), + generators.listOf(new ExportTrialLabelCriterion( + LongId[Criterion](generators.nextInt(999999).toLong), + generators.nextOption(generators.nextBoolean()), + LongId[Label](generators.nextInt(999999).toLong), + generators.setOf(LongId[Arm](generators.nextInt(999999).toLong)), + generators.nextName().value, + generators.nextBoolean(), + generators.nextBoolean() + )) + ))) + } + def update(origTrial: Trial, draftTrial: Trial)( implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] = Future.successful(UpdateReply.Updated(draftTrial)) -- cgit v1.2.3 From a6cc13dfcc9334225712744d458b77de0a89dd7d Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 21 Jul 2017 16:49:50 -0700 Subject: Implementing new method in FakeTrialService --- src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main/scala/xyz/driver/pdsuidomain') diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala index b2a140c..0164263 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala @@ -88,7 +88,7 @@ trait PatientService { def getById(id: UuidId[Patient])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] - def getExportPatient(id: UuidId[Patient])( + def getPatientWithLabels(id: UuidId[Patient])( implicit requestContext: AuthenticatedRequestContext): Future[GetPatientWithLabelsReply] def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty, -- cgit v1.2.3 From ef8372d847d4d28922f6426830c31883c2a9c243 Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 21 Jul 2017 17:05:20 -0700 Subject: Moving export method to ExtractedDataService --- .../pdsuidomain/services/ExtractedDataService.scala | 17 ++++++++++++++++- .../driver/pdsuidomain/services/PatientService.scala | 15 --------------- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'src/main/scala/xyz/driver/pdsuidomain') diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/ExtractedDataService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/ExtractedDataService.scala index afb994e..9bcd921 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/ExtractedDataService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/ExtractedDataService.scala @@ -2,10 +2,11 @@ package xyz.driver.pdsuidomain.services import xyz.driver.pdsuicommon.auth.AuthenticatedRequestContext import xyz.driver.pdsuicommon.db.{Pagination, SearchFilterExpr, Sorting} -import xyz.driver.pdsuicommon.domain.LongId +import xyz.driver.pdsuicommon.domain.{LongId, UuidId} import xyz.driver.pdsuicommon.error.DomainError import xyz.driver.pdsuicommon.logging._ import xyz.driver.pdsuidomain.entities._ +import xyz.driver.pdsuidomain.entities.export.patient.ExportPatientWithLabels import scala.concurrent.Future @@ -49,6 +50,17 @@ object ExtractedDataService { extends GetListReply with DomainError.AuthorizationError with DefaultAccessDeniedError } + sealed trait GetPatientLabelsReply + object GetPatientLabelsReply { + type Error = GetPatientLabelsReply with DomainError + + final case class Entity(x: ExportPatientWithLabels) extends GetPatientLabelsReply + + case object NotFoundError extends GetPatientLabelsReply with DomainError.NotFoundError { + def userMessage: String = "Patient not found" + } + } + sealed trait CreateReply object CreateReply { type Error = CreateReply with DomainError @@ -93,6 +105,9 @@ trait ExtractedDataService { def getById(id: LongId[ExtractedData])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] + def getPatientLabels(id: UuidId[Patient])( + implicit requestContext: AuthenticatedRequestContext): Future[GetPatientLabelsReply] + def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty, sorting: Option[Sorting] = None, pagination: Option[Pagination] = None)( diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala index 0164263..ce70934 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/PatientService.scala @@ -8,7 +8,6 @@ import xyz.driver.pdsuicommon.domain._ import xyz.driver.pdsuicommon.error.DomainError import xyz.driver.pdsuicommon.logging._ import xyz.driver.pdsuidomain.entities._ -import xyz.driver.pdsuidomain.entities.export.patient.ExportPatientWithLabels import scala.concurrent.Future @@ -51,17 +50,6 @@ object PatientService { } } - sealed trait GetPatientWithLabelsReply - object GetPatientWithLabelsReply { - type Error = GetPatientWithLabelsReply with DomainError - - final case class Entity(x: ExportPatientWithLabels) extends GetPatientWithLabelsReply - - case object NotFoundError extends GetPatientWithLabelsReply with DomainError.NotFoundError { - def userMessage: String = "Patient not found" - } - } - sealed trait UpdateReply object UpdateReply { type Error = UpdateReply with DomainError @@ -88,9 +76,6 @@ trait PatientService { def getById(id: UuidId[Patient])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] - def getPatientWithLabels(id: UuidId[Patient])( - implicit requestContext: AuthenticatedRequestContext): Future[GetPatientWithLabelsReply] - def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty, sorting: Option[Sorting] = None, pagination: Option[Pagination] = None)( -- cgit v1.2.3 From 92f503ce18e90c9955bed8b0a8c6ec2e30f0304e Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 21 Jul 2017 17:51:57 -0700 Subject: New types of Medical Records errors --- .../xyz/driver/pdsuidomain/services/MedicalRecordService.scala | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/main/scala/xyz/driver/pdsuidomain') diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/MedicalRecordService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/MedicalRecordService.scala index cfdb2b1..46e9156 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/MedicalRecordService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/MedicalRecordService.scala @@ -60,8 +60,14 @@ object MedicalRecordService { final case class EntityList(xs: Seq[MedicalRecord], totalFound: Int, lastUpdate: Option[LocalDateTime]) extends GetListReply + type Error = GetListReply with DomainError + case object AuthorizationError extends GetListReply with DomainError.AuthorizationError with DefaultAccessDeniedError + + case object NotFoundError extends GetListReply with DomainError.NotFoundError { + def userMessage: String = "Patient wasn't found" + } } sealed trait CreateReply -- cgit v1.2.3