From 1f941bc2722a94e3a1b30d2aea7d7acbb3789aaf Mon Sep 17 00:00:00 2001 From: Vyatcheslav Suharnikov Date: Wed, 5 Jul 2017 10:45:53 +0300 Subject: PDSUI-2013 Create Trial Curation's own messages table for trials --- .../scala/xyz/driver/pdsuicommon/acl/ACL.scala | 17 +++- .../driver/pdsuidomain/entities/TrialIssue.scala | 23 ++++++ .../json/trialissue/ApiPartialTrialIssue.scala | 40 ++++++++++ .../formats/json/trialissue/ApiTrialIssue.scala | 42 ++++++++++ .../pdsuidomain/services/TrialIssueService.scala | 93 ++++++++++++++++++++++ 5 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 src/main/scala/xyz/driver/pdsuidomain/entities/TrialIssue.scala create mode 100644 src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue/ApiPartialTrialIssue.scala create mode 100644 src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue/ApiTrialIssue.scala create mode 100644 src/main/scala/xyz/driver/pdsuidomain/services/TrialIssueService.scala (limited to 'src/main/scala/xyz/driver') diff --git a/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala b/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala index 2a504fb..f201554 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/acl/ACL.scala @@ -96,10 +96,10 @@ object ACL extends PhiLogging { object Message extends BaseACL( label = "message", - create = RepRoles ++ TreatmentMatchingRoles ++ TcRoles, - read = RepRoles ++ TreatmentMatchingRoles ++ TcRoles, - update = RepRoles ++ TreatmentMatchingRoles ++ TcRoles, - delete = RepRoles ++ TreatmentMatchingRoles ++ TcRoles + create = RepRoles ++ TreatmentMatchingRoles, + read = RepRoles ++ TreatmentMatchingRoles, + update = RepRoles ++ TreatmentMatchingRoles, + delete = RepRoles ++ TreatmentMatchingRoles ) // TC @@ -117,6 +117,15 @@ object ACL extends PhiLogging { read = Set(TrialAdmin) ) + object TrialIssue + extends BaseACL( + label = "trial issue", + create = TcRoles, + read = TcRoles, + update = TcRoles, + delete = TcRoles + ) + object StudyDesign extends BaseACL( label = "study design", diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/TrialIssue.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/TrialIssue.scala new file mode 100644 index 0000000..34895b6 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/TrialIssue.scala @@ -0,0 +1,23 @@ +package xyz.driver.pdsuidomain.entities + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.domain.{LongId, StringId, User} +import xyz.driver.pdsuicommon.logging._ + +final case class TrialIssue(id: LongId[TrialIssue], + userId: LongId[User], + trialId: StringId[Trial], + lastUpdate: LocalDateTime, + isDraft: Boolean, + text: String, + evidence: String, + archiveRequired: Boolean, + meta: String) + +object TrialIssue { + implicit def toPhiString(x: TrialIssue): PhiString = { + import x._ + phi"TrialIssue(id=$id, userId=$userId, trialId=$trialId)" + } +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue/ApiPartialTrialIssue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue/ApiPartialTrialIssue.scala new file mode 100644 index 0000000..5dcde8a --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue/ApiPartialTrialIssue.scala @@ -0,0 +1,40 @@ +package xyz.driver.pdsuidomain.formats.json.trialissue + +import java.time.LocalDateTime + +import play.api.libs.functional.syntax._ +import play.api.libs.json._ +import xyz.driver.pdsuicommon.domain.{LongId, StringId, User} +import xyz.driver.pdsuidomain.entities.{Trial, TrialIssue} + +final case class ApiPartialTrialIssue(text: String, evidence: String, archiveRequired: Boolean, meta: String) { + def applyTo(x: TrialIssue): TrialIssue = x.copy( + text = text, + evidence = evidence, + archiveRequired = archiveRequired, + meta = meta + ) + + def toDomain(userId: LongId[User], trialId: StringId[Trial]) = TrialIssue( + id = LongId(0), + userId = userId, + trialId = trialId, + lastUpdate = LocalDateTime.MIN, + isDraft = true, + text = text, + evidence = evidence, + archiveRequired = false, + meta = meta + ) +} + +object ApiPartialTrialIssue { + implicit val format: Format[ApiPartialTrialIssue] = ( + (JsPath \ "text").format[String] and + (JsPath \ "evidence").format[String] and + (JsPath \ "archiveRequired").format[Boolean] and + (JsPath \ "meta").format[String](Format(Reads { x => + JsSuccess(Json.stringify(x)) + }, Writes[String](Json.parse))) + )(ApiPartialTrialIssue.apply, unlift(ApiPartialTrialIssue.unapply)) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue/ApiTrialIssue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue/ApiTrialIssue.scala new file mode 100644 index 0000000..a5399aa --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue/ApiTrialIssue.scala @@ -0,0 +1,42 @@ +package xyz.driver.pdsuidomain.formats.json.trialissue + +import java.time.{ZoneId, ZonedDateTime} + +import play.api.libs.functional.syntax._ +import play.api.libs.json._ +import xyz.driver.pdsuidomain.entities.TrialIssue + +final case class ApiTrialIssue(id: Long, + text: String, + lastUpdate: ZonedDateTime, + userId: Long, + isDraft: Boolean, + evidence: String, + archiveRequired: Boolean, + meta: String) + +object ApiTrialIssue { + implicit val format: Format[ApiTrialIssue] = ( + (JsPath \ "id").format[Long] and + (JsPath \ "text").format[String] and + (JsPath \ "lastUpdate").format[ZonedDateTime] and + (JsPath \ "userId").format[Long] and + (JsPath \ "isDraft").format[Boolean] and + (JsPath \ "evidence").format[String] and + (JsPath \ "archiveRequired").format[Boolean] and + (JsPath \ "meta").format[String](Format(Reads { x => + JsSuccess(Json.stringify(x)) + }, Writes[String](Json.parse))) + )(ApiTrialIssue.apply, unlift(ApiTrialIssue.unapply)) + + def fromDomain(x: TrialIssue) = ApiTrialIssue( + id = x.id.id, + text = x.text, + lastUpdate = ZonedDateTime.of(x.lastUpdate, ZoneId.of("Z")), + userId = x.userId.id, + isDraft = x.isDraft, + evidence = x.evidence, + archiveRequired = x.archiveRequired, + meta = x.meta + ) +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/TrialIssueService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/TrialIssueService.scala new file mode 100644 index 0000000..df231ac --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/services/TrialIssueService.scala @@ -0,0 +1,93 @@ +package xyz.driver.pdsuidomain.services + +import java.time.LocalDateTime + +import xyz.driver.pdsuicommon.auth.AuthenticatedRequestContext +import xyz.driver.pdsuicommon.db.{Pagination, SearchFilterExpr, Sorting} +import xyz.driver.pdsuicommon.domain.{LongId, StringId} +import xyz.driver.pdsuicommon.error.DomainError +import xyz.driver.pdsuidomain.entities.{Trial, TrialIssue} + +import scala.concurrent.Future + +object TrialIssueService { + + trait DefaultNotFoundError { + def userMessage: String = "TrialIssue not found" + } + + trait DefaultAccessDeniedError { + def userMessage: String = "Access denied" + } + + sealed trait CreateReply + object CreateReply { + type Error = CreateReply with DomainError + case class Created(x: TrialIssue) extends CreateReply + case object AuthorizationError + extends CreateReply with DomainError.AuthorizationError with DefaultAccessDeniedError + case class CommonError(userMessage: String) extends CreateReply with DomainError + } + + sealed trait GetByIdReply + object GetByIdReply { + type Error = GetByIdReply with DomainError + case class Entity(x: TrialIssue) extends GetByIdReply + case object NotFoundError extends GetByIdReply with DomainError.NotFoundError with DefaultNotFoundError + case class CommonError(userMessage: String) extends GetByIdReply with DomainError + case object AuthorizationError + extends GetByIdReply with DomainError.AuthorizationError with DefaultAccessDeniedError + } + + sealed trait GetListByTrialIdReply + object GetListByTrialIdReply { + type Error = GetListByTrialIdReply with DomainError + case class EntityList(xs: Seq[TrialIssue], totalFound: Int, lastUpdate: Option[LocalDateTime]) + extends GetListByTrialIdReply + case object NotFoundError extends GetListByTrialIdReply with DomainError.NotFoundError with DefaultNotFoundError + case object AuthorizationError + extends GetListByTrialIdReply with DomainError.AuthorizationError with DefaultAccessDeniedError + } + + sealed trait UpdateReply + object UpdateReply { + type Error = UpdateReply with DomainError + case class Updated(updated: TrialIssue) extends UpdateReply + case object AuthorizationError + extends UpdateReply with DomainError.AuthorizationError with DefaultAccessDeniedError + 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 object AuthorizationError + extends DeleteReply with DomainError.AuthorizationError with DefaultAccessDeniedError + case object NotFoundError extends DeleteReply with DomainError.NotFoundError with DefaultNotFoundError + case class CommonError(userMessage: String) extends DeleteReply with DomainError + } +} + +trait TrialIssueService { + + import TrialIssueService._ + + def create(draft: TrialIssue)(implicit requestContext: AuthenticatedRequestContext): Future[CreateReply] + + def getById(trialId: StringId[Trial], id: LongId[TrialIssue])( + implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] + + def getListByTrialId(trialId: StringId[Trial], + filter: SearchFilterExpr = SearchFilterExpr.Empty, + sorting: Option[Sorting] = None, + pagination: Option[Pagination] = None)( + implicit requestContext: AuthenticatedRequestContext): Future[GetListByTrialIdReply] + + def update(orig: TrialIssue, draft: TrialIssue)( + implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] + + def delete(trialId: StringId[Trial], id: LongId[TrialIssue])( + implicit requestContext: AuthenticatedRequestContext): Future[DeleteReply] + +} -- cgit v1.2.3