aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/pdsuidomain/services/rest
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuidomain/services/rest')
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala99
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala108
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala139
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala39
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala78
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala40
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala103
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala37
-rw-r--r--src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala117
9 files changed, 760 insertions, 0 deletions
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala
new file mode 100644
index 0000000..299e6f8
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala
@@ -0,0 +1,99 @@
+package xyz.driver.pdsuidomain.services.rest
+
+import akka.http.scaladsl.marshalling.Marshal
+import scala.concurrent.{ExecutionContext, Future}
+
+import akka.http.scaladsl.model._
+import akka.stream.ActorMaterializer
+import xyz.driver.core.rest._
+import xyz.driver.pdsuicommon.auth._
+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.arm.ApiArm
+import xyz.driver.pdsuidomain.services.ArmService
+
+class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protected val materializer: ActorMaterializer,
+ protected val exec: ExecutionContext)
+ extends ArmService with RestHelper {
+
+ import xyz.driver.pdsuidomain.services.ArmService._
+ import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._
+
+ // GET /v1/arm xyz.driver.server.controllers.ArmController.getList
+ // GET /v1/arm/:id xyz.driver.server.controllers.ArmController.getById(id: Long)
+ // POST /v1/arm xyz.driver.server.controllers.ArmController.create
+ // PATCH /v1/arm/:id xyz.driver.server.controllers.ArmController.update(id: Long)
+ // DELETE /v1/arm/:id xyz.driver.server.controllers.ArmController.delete(id: Long)
+
+ def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Option[Sorting] = None,
+ pagination: Option[Pagination] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = {
+
+ val request = HttpRequest(
+ HttpMethods.GET,
+ endpointUri(baseUri, "/v1/arm", filterQuery(filter) ++ sortingQuery(sorting) ++ paginationQuery(pagination)))
+
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ListResponse[ApiArm], GetListReply](response) { api =>
+ GetListReply.EntityList(api.items.map(_.toDomain), api.meta.itemsCount)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def getById(armId: LongId[Arm])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] = {
+ val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/arm/$armId"))
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiArm, GetByIdReply](response) { api =>
+ GetByIdReply.Entity(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def create(draftArm: Arm)(implicit requestContext: AuthenticatedRequestContext): Future[CreateReply] = {
+ for {
+ entity <- Marshal(ApiArm.fromDomain(draftArm)).to[RequestEntity]
+ request = HttpRequest(HttpMethods.POST, endpointUri(baseUri, "/v1/arm")).withEntity(entity)
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiArm, CreateReply](response) { api =>
+ CreateReply.Created(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def update(origArm: Arm, draftArm: Arm)(implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] = {
+ val id = origArm.id
+ val request = HttpRequest(HttpMethods.PATCH, endpointUri(baseUri, s"/v1/arm/$id"))
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiArm, UpdateReply](response) { api =>
+ UpdateReply.Updated(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def delete(id: LongId[Arm])(implicit requestContext: AuthenticatedRequestContext): Future[DeleteReply] = {
+ val request = HttpRequest(HttpMethods.DELETE, endpointUri(baseUri, s"/v1/arm/$id"))
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiArm, DeleteReply](response) { _ =>
+ DeleteReply.Deleted
+ }()
+ } yield {
+ reply
+ }
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala
new file mode 100644
index 0000000..b27ce3d
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala
@@ -0,0 +1,108 @@
+package xyz.driver.pdsuidomain.services.rest
+
+import scala.concurrent.{ExecutionContext, Future}
+
+import akka.http.scaladsl.marshalling.Marshal
+import akka.http.scaladsl.model._
+import akka.stream.ActorMaterializer
+import xyz.driver.core.rest._
+import xyz.driver.pdsuicommon.auth._
+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.services.CriterionService
+
+class RestCriterionService(transport: ServiceTransport, baseUri: Uri)(
+ implicit protected val materializer: ActorMaterializer,
+ protected val exec: ExecutionContext)
+ extends CriterionService with RestHelper {
+
+ import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._
+ import xyz.driver.pdsuidomain.formats.json.criterion.ApiCriterion
+ import xyz.driver.pdsuidomain.services.CriterionService._
+
+ // GET /v1/criterion xyz.driver.server.controllers.CriterionController.getList
+ // GET /v1/criterion/:id xyz.driver.server.controllers.CriterionController.getById(id: Long)
+ // PATCH /v1/criterion/:id xyz.driver.server.controllers.CriterionController.update(id: Long)
+ // POST /v1/criterion xyz.driver.server.controllers.CriterionController.create
+ // DELETE /v1/criterion/:id xyz.driver.server.controllers.CriterionController.delete(id: Long)
+
+ def create(draftRichCriterion: RichCriterion)(
+ implicit requestContext: AuthenticatedRequestContext): Future[CreateReply] = {
+ for {
+ entity <- Marshal(ApiCriterion.fromDomain(draftRichCriterion)).to[RequestEntity]
+ request = HttpRequest(HttpMethods.POST, endpointUri(baseUri, "/v1/criterion")).withEntity(entity)
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiCriterion, CreateReply](response) { api =>
+ CreateReply.Created(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def getById(id: LongId[Criterion])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] = {
+ val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/criterion/$id"))
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiCriterion, GetByIdReply](response) { api =>
+ GetByIdReply.Entity(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Option[Sorting] = None,
+ pagination: Option[Pagination] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = {
+
+ val request = HttpRequest(HttpMethods.GET,
+ endpointUri(baseUri,
+ s"/v1/criterion",
+ filterQuery(filter) ++ sortingQuery(sorting) ++ paginationQuery(pagination)))
+
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ListResponse[ApiCriterion], GetListReply](response) { api =>
+ GetListReply.EntityList(
+ api.items.map(_.toDomain),
+ api.meta.itemsCount,
+ api.meta.lastUpdate
+ )
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def update(origRichCriterion: RichCriterion, draftRichCriterion: RichCriterion)(
+ implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] = {
+ val id = origRichCriterion.criterion.id
+ for {
+ entity <- Marshal(ApiCriterion.fromDomain(draftRichCriterion)).to[RequestEntity]
+ request = HttpRequest(HttpMethods.PATCH, endpointUri(baseUri, s"/v1/criterion/$id")).withEntity(entity)
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiCriterion, UpdateReply](response) { api =>
+ UpdateReply.Updated(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def delete(id: LongId[Criterion])(implicit requestContext: AuthenticatedRequestContext): Future[DeleteReply] = {
+ val request = HttpRequest(HttpMethods.DELETE, endpointUri(baseUri, s"/v1/criterion/$id"))
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiCriterion, DeleteReply](response) { _ =>
+ DeleteReply.Deleted
+ }()
+ } yield {
+ reply
+ }
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala
new file mode 100644
index 0000000..7d2838b
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala
@@ -0,0 +1,139 @@
+package xyz.driver.pdsuidomain.services.rest
+
+import java.lang.RuntimeException
+
+import scala.concurrent.{ExecutionContext, Future}
+
+import akka.http.scaladsl.model.{HttpMethods, HttpRequest, HttpResponse, ResponseEntity, StatusCode, StatusCodes, Uri}
+import akka.http.scaladsl.unmarshalling.{Unmarshal, Unmarshaller}
+import akka.stream.Materializer
+import xyz.driver.core.rest.ServiceRequestContext
+import xyz.driver.pdsuicommon.auth.{AnonymousRequestContext, AuthenticatedRequestContext}
+import xyz.driver.pdsuicommon.db.{
+ Pagination,
+ SearchFilterBinaryOperation,
+ SearchFilterExpr,
+ SearchFilterNAryOperation,
+ Sorting,
+ SortingOrder
+}
+import xyz.driver.pdsuicommon.error.DomainError
+
+trait RestHelper {
+
+ implicit protected val materializer: Materializer
+ implicit protected val exec: ExecutionContext
+
+ protected def endpointUri(baseUri: Uri, path: String) =
+ baseUri.withPath(Uri.Path(path))
+
+ protected def endpointUri(baseUri: Uri, path: String, query: Seq[(String, String)]) =
+ baseUri.withPath(Uri.Path(path)).withQuery(Uri.Query(query: _*))
+
+ def get(baseUri: Uri, path: String, query: Seq[(String, String)] = Seq.empty): HttpRequest =
+ HttpRequest(HttpMethods.GET, endpointUri(baseUri, path, query))
+
+ def sortingQuery(sorting: Option[Sorting]): Seq[(String, String)] = {
+ def dimensionQuery(dimension: Sorting.Dimension) = {
+ val ord = dimension.order match {
+ case SortingOrder.Ascending => ""
+ case SortingOrder.Descending => "-"
+ }
+ s"$ord${dimension.name}"
+ }
+
+ sorting match {
+ case None => Seq.empty
+ case Some(dimension: Sorting.Dimension) => Seq("sort" -> dimensionQuery(dimension))
+ case Some(Sorting.Sequential(dimensions)) => Seq("sort" -> dimensions.map(dimensionQuery).mkString(","))
+ }
+ }
+
+ def filterQuery(expr: SearchFilterExpr): Seq[(String, String)] = {
+ def opToString(op: SearchFilterBinaryOperation) = op match {
+ case SearchFilterBinaryOperation.Eq => "eq"
+ case SearchFilterBinaryOperation.NotEq => "ne"
+ case SearchFilterBinaryOperation.Like => "like"
+ case SearchFilterBinaryOperation.Gt => "gt"
+ case SearchFilterBinaryOperation.GtEq => "ge"
+ case SearchFilterBinaryOperation.Lt => "lt"
+ case SearchFilterBinaryOperation.LtEq => "le"
+ }
+
+ def exprToQuery(expr: SearchFilterExpr): Seq[(String, String)] = expr match {
+ case SearchFilterExpr.Empty => Seq.empty
+ case SearchFilterExpr.Atom.Binary(dimension, op, value) =>
+ Seq("filters" -> s"${dimension.name} ${opToString(op)} $value")
+ case SearchFilterExpr.Atom.NAry(dimension, SearchFilterNAryOperation.In, values) =>
+ Seq("filters" -> s"${dimension.name} in ${values.mkString(",")}")
+ case SearchFilterExpr.Intersection(ops) =>
+ ops.flatMap(op => exprToQuery(op))
+ case expr => sys.error(s"No parser available for filter expression $expr.")
+ }
+
+ exprToQuery(expr)
+ }
+
+ def paginationQuery(pagination: Option[Pagination]): Seq[(String, String)] = pagination match {
+ case None => Seq.empty
+ case Some(pp) =>
+ Seq(
+ "pageNumber" -> pp.pageNumber.toString,
+ "pageSize" -> pp.pageSize.toHexString
+ )
+ }
+
+ /** Utility method to parse responses that encode success and errors as subtypes
+ * of a common reply type.
+ *
+ * @tparam ApiReply The type of the serialized reply object, contained in the HTTP entity
+ * @tparam DomainReply The type of the domain object that will be created from a successful reply.
+ *
+ * @param response The HTTP response to parse.
+ * @param successMapper Transformation function from a deserialized api entity to a domain object.
+ * @param errorMapper Transformation function from general domain errors to
+ * specialized errors of the given DomainReply. Note that if a domain error
+ * is not explicitly handled, it will be encoded as a failure in the returned future.
+ * @param unmarshaller An unmarshaller that converts a successful response to an api reply.
+ */
+ def apiResponse[ApiReply, DomainReply](response: HttpResponse)(successMapper: ApiReply => DomainReply)(
+ errorMapper: PartialFunction[DomainError, DomainReply] = PartialFunction.empty)(
+ implicit unmarshaller: Unmarshaller[ResponseEntity, ApiReply]): Future[DomainReply] = {
+
+ val domainErrors: Map[StatusCode, DomainError] = Map(
+ StatusCodes.Unauthorized -> new DomainError.AuthenticationError {
+ override protected def userMessage: String = "unauthorized"
+ }, // 401
+ StatusCodes.Forbidden -> new DomainError.AuthorizationError {
+ override protected def userMessage: String = "forbidden"
+ }, // 403
+ StatusCodes.NotFound -> new DomainError.NotFoundError {
+ override protected def userMessage: String = "not found"
+ } // 404
+ )
+
+ if (response.status.isSuccess) {
+ val reply = Unmarshal(response.entity).to[ApiReply]
+ reply.map(successMapper)
+ } else {
+ val domainError = domainErrors.get(response.status)
+ domainError.flatMap(errorMapper.lift) match {
+ case Some(error) => Future.successful(error)
+ case None =>
+ Future.failed(
+ new RuntimeException(
+ s"Unhandled domain error for HTTP status ${response.status}. Message ${response.entity}")
+ )
+ }
+ }
+ }
+
+ implicit def toServiceRequestContext(requestContext: AnonymousRequestContext): ServiceRequestContext = {
+ val auth: Map[String, String] = requestContext match {
+ case ctx: AuthenticatedRequestContext => Map("Auth-token" -> ctx.authToken)
+ case _ => Map()
+ }
+ new ServiceRequestContext(contextHeaders = auth)
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala
new file mode 100644
index 0000000..ff9d490
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala
@@ -0,0 +1,39 @@
+package xyz.driver.pdsuidomain.services.rest
+
+import scala.concurrent.{ExecutionContext, Future}
+
+import akka.http.scaladsl.model._
+import akka.stream.ActorMaterializer
+import xyz.driver.core.rest._
+import xyz.driver.pdsuicommon.auth._
+import xyz.driver.pdsuicommon.db._
+import xyz.driver.pdsuidomain.formats.json.ListResponse
+import xyz.driver.pdsuidomain.formats.json.hypothesis.ApiHypothesis
+import xyz.driver.pdsuidomain.services.HypothesisService
+
+class RestHypothesisService(transport: ServiceTransport, baseUri: Uri)(
+ implicit protected val materializer: ActorMaterializer,
+ protected val exec: ExecutionContext)
+ extends HypothesisService with RestHelper {
+
+ import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._
+ import xyz.driver.pdsuidomain.services.HypothesisService._
+
+ // GET /v1/hypothesis xyz.driver.server.controllers.HypothesisController.getList
+
+ def getAll(sorting: Option[Sorting] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = {
+
+ val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, "/v1/hypothesis", sortingQuery(sorting)))
+
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ListResponse[ApiHypothesis], GetListReply](response) { api =>
+ GetListReply.EntityList(api.items.map(_.toDomain), api.meta.itemsCount)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala
new file mode 100644
index 0000000..810a9d6
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala
@@ -0,0 +1,78 @@
+package xyz.driver.pdsuidomain.services.rest
+
+import akka.http.scaladsl.marshalling.Marshal
+import scala.concurrent.{ExecutionContext, Future}
+
+import akka.http.scaladsl.model._
+import akka.stream.ActorMaterializer
+import xyz.driver.core.rest._
+import xyz.driver.pdsuicommon.auth._
+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.intervention.ApiIntervention
+import xyz.driver.pdsuidomain.services.InterventionService
+
+class RestInterventionService(transport: ServiceTransport, baseUri: Uri)(
+ implicit protected val materializer: ActorMaterializer,
+ protected val exec: ExecutionContext)
+ extends InterventionService with RestHelper {
+
+ import xyz.driver.pdsuidomain.services.InterventionService._
+ import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._
+
+ // GET /v1/intervention xyz.driver.server.controllers.InterventionController.getList
+ // GET /v1/intervention/:id xyz.driver.server.controllers.InterventionController.getById(id: Long)
+ // PATCH /v1/intervention/:id xyz.driver.server.controllers.InterventionController.update(id: Long)
+
+ def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Option[Sorting] = None,
+ pagination: Option[Pagination] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = {
+
+ val request = HttpRequest(HttpMethods.GET,
+ endpointUri(baseUri,
+ "/v1/intervention",
+ filterQuery(filter) ++ sortingQuery(sorting) ++ paginationQuery(pagination)))
+
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ListResponse[ApiIntervention], GetListReply](response) { api =>
+ GetListReply.EntityList(api.items.map(_.toDomain), api.meta.itemsCount)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def getById(id: LongId[Intervention])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] = {
+ val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/intervention/$id"))
+
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiIntervention, GetByIdReply](response) { api =>
+ GetByIdReply.Entity(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def update(origIntervention: InterventionWithArms, draftIntervention: InterventionWithArms)(
+ implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] = {
+ val id = origIntervention.intervention.id
+
+ for {
+ entity <- Marshal(ApiIntervention.fromDomain(draftIntervention)).to[RequestEntity]
+ request = HttpRequest(HttpMethods.PATCH, endpointUri(baseUri, s"/v1/intervention/$id")).withEntity(entity)
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiIntervention, UpdateReply](response) { api =>
+ UpdateReply.Updated(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala
new file mode 100644
index 0000000..1243500
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala
@@ -0,0 +1,40 @@
+package xyz.driver.pdsuidomain.services.rest
+
+import scala.concurrent.{ExecutionContext, Future}
+
+import akka.http.scaladsl.model._
+import akka.stream.ActorMaterializer
+import xyz.driver.core.rest._
+import xyz.driver.pdsuicommon.auth._
+import xyz.driver.pdsuicommon.db._
+import xyz.driver.pdsuicommon.error.DomainError
+import xyz.driver.pdsuidomain.formats.json.intervention.ApiInterventionType
+import xyz.driver.pdsuidomain.services.InterventionTypeService
+
+class RestInterventionTypeService(transport: ServiceTransport, baseUri: Uri)(
+ implicit protected val materializer: ActorMaterializer,
+ protected val exec: ExecutionContext)
+ extends InterventionTypeService with RestHelper {
+
+ import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._
+ import xyz.driver.pdsuidomain.formats.json.ListResponse
+ import xyz.driver.pdsuidomain.services.InterventionTypeService._
+
+ def getAll(sorting: Option[Sorting] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = {
+
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(
+ get(baseUri, "/v1/intervention-type", query = sortingQuery(sorting)))
+ reply <- apiResponse[ListResponse[ApiInterventionType], GetListReply](response) { list =>
+ val domain = list.items.map(_.toDomain)
+ GetListReply.EntityList(domain.toList, list.meta.itemsCount)
+ } {
+ case _: DomainError.AuthorizationError => GetListReply.AuthorizationError
+ }
+ } yield {
+ reply
+ }
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala
new file mode 100644
index 0000000..61d2050
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala
@@ -0,0 +1,103 @@
+package xyz.driver.pdsuidomain.services.rest
+
+import akka.http.scaladsl.marshalling.Marshal
+import scala.concurrent.{ExecutionContext, Future}
+
+import akka.http.scaladsl.model._
+import akka.stream.ActorMaterializer
+import xyz.driver.core.rest._
+import xyz.driver.pdsuicommon.auth._
+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.message.ApiMessage
+import xyz.driver.pdsuidomain.services.MessageService
+
+class RestMessageService(transport: ServiceTransport, baseUri: Uri)(
+ implicit protected val materializer: ActorMaterializer,
+ protected val exec: ExecutionContext)
+ extends MessageService with RestHelper {
+
+ import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._
+ import xyz.driver.pdsuidomain.services.MessageService._
+
+ // GET /v1/message xyz.driver.server.controllers.MessageController.getList
+ // POST /v1/message xyz.driver.server.controllers.MessageController.create
+ // PATCH /v1/message/:id xyz.driver.server.controllers.MessageController.update(id: Long)
+ // DELETE /v1/message/:id xyz.driver.server.controllers.MessageController.delete(id: Long)
+
+ def create(draftMessage: Message)(implicit requestContext: AuthenticatedRequestContext): Future[CreateReply] = {
+ for {
+ entity <- Marshal(ApiMessage.fromDomain(draftMessage)).to[RequestEntity]
+ request = HttpRequest(HttpMethods.POST, endpointUri(baseUri, "/v1/message")).withEntity(entity)
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiMessage, CreateReply](response) { api =>
+ CreateReply.Created(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def getById(messageId: LongId[Message])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] = {
+ import SearchFilterExpr._
+ import SearchFilterBinaryOperation._
+ val filter = Atom.Binary("id", Eq, messageId)
+ getAll(filter).map {
+ case GetListReply.EntityList(messages, _, _) if messages.isEmpty =>
+ GetByIdReply.NotFoundError
+ case GetListReply.EntityList(messages, _, _) =>
+ GetByIdReply.Entity(messages.head)
+ case GetListReply.AuthorizationError =>
+ GetByIdReply.AuthorizationError
+ }
+ }
+ def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Option[Sorting] = None,
+ pagination: Option[Pagination] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = {
+
+ val request = HttpRequest(
+ HttpMethods.GET,
+ endpointUri(baseUri, "/v1/message", filterQuery(filter) ++ sortingQuery(sorting) ++ paginationQuery(pagination)))
+
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ListResponse[ApiMessage], GetListReply](response) { api =>
+ GetListReply.EntityList(api.items.map(_.toDomain), api.meta.itemsCount, api.meta.lastUpdate)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def update(origMessage: Message, draftMessage: Message)(
+ implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] = {
+ for {
+ entity <- Marshal(ApiMessage.fromDomain(draftMessage)).to[RequestEntity]
+ id = origMessage.id.id
+ request = HttpRequest(HttpMethods.PATCH, endpointUri(baseUri, s"/v1/message/$id")).withEntity(entity)
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiMessage, UpdateReply](response) { api =>
+ UpdateReply.Updated(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def delete(messageId: LongId[Message])(implicit requestContext: AuthenticatedRequestContext): Future[DeleteReply] = {
+ val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/message/${messageId.id}"))
+
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiMessage, DeleteReply](response) { _ =>
+ DeleteReply.Deleted
+ }()
+ } yield {
+ reply
+ }
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala
new file mode 100644
index 0000000..66f7a78
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala
@@ -0,0 +1,37 @@
+package xyz.driver.pdsuidomain.services.rest
+
+import scala.concurrent.{ExecutionContext, Future}
+
+import akka.http.scaladsl.model._
+import akka.stream.ActorMaterializer
+import xyz.driver.core.rest._
+import xyz.driver.pdsuicommon.auth._
+import xyz.driver.pdsuicommon.db._
+import xyz.driver.pdsuidomain.formats.json.ListResponse
+import xyz.driver.pdsuidomain.formats.json.studydesign.ApiStudyDesign
+import xyz.driver.pdsuidomain.services.StudyDesignService
+
+class RestStudyDesignService(transport: ServiceTransport, baseUri: Uri)(
+ implicit protected val materializer: ActorMaterializer,
+ protected val exec: ExecutionContext)
+ extends StudyDesignService with RestHelper {
+
+ import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._
+ import xyz.driver.pdsuidomain.services.StudyDesignService._
+
+ // GET /v1/study-design xyz.driver.server.controllers.StudyDesignController.getList
+
+ def getAll(sorting: Option[Sorting] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = {
+ val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, "/v1/study-design", sortingQuery(sorting)))
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ListResponse[ApiStudyDesign], GetListReply](response) { api =>
+ GetListReply.EntityList(api.items.map(_.toDomain), api.meta.itemsCount)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala
new file mode 100644
index 0000000..9b4b576
--- /dev/null
+++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala
@@ -0,0 +1,117 @@
+package xyz.driver.pdsuidomain.services.rest
+
+import akka.http.scaladsl.marshalling.Marshal
+import scala.NotImplementedError
+import scala.concurrent.{ExecutionContext, Future}
+
+import akka.http.scaladsl.model._
+import akka.stream.ActorMaterializer
+import xyz.driver.core.rest._
+import xyz.driver.pdsuicommon.auth._
+import xyz.driver.pdsuicommon.db._
+import xyz.driver.pdsuicommon.domain._
+import xyz.driver.pdsuidomain.entities._
+import xyz.driver.pdsuidomain.formats.json.trial.ApiTrial
+import xyz.driver.pdsuidomain.services.TrialService
+import xyz.driver.pdsuidomain.formats.json.ListResponse
+
+class RestTrialService(transport: ServiceTransport, baseUri: Uri)(
+ implicit protected val materializer: ActorMaterializer,
+ protected val exec: ExecutionContext)
+ extends TrialService with RestHelper {
+
+ import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._
+ import xyz.driver.pdsuidomain.services.TrialService._
+
+ // GET /v1/trial xyz.driver.server.controllers.TrialController.getList
+ // GET /v1/trial/:id xyz.driver.server.controllers.TrialController.getById(id: String)
+ // GET /v1/trial/:id/source xyz.driver.server.controllers.TrialController.getSource(id: String)
+ // PATCH /v1/trial/:id xyz.driver.server.controllers.TrialController.update(id: String)
+ // POST /v1/trial/:id/start xyz.driver.server.controllers.TrialController.start(id: String)
+ // POST /v1/trial/:id/submit xyz.driver.server.controllers.TrialController.submit(id: String)
+ // POST /v1/trial/:id/restart xyz.driver.server.controllers.TrialController.restart(id: String)
+ // POST /v1/trial/:id/flag xyz.driver.server.controllers.TrialController.flag(id: String)
+ // POST /v1/trial/:id/resolve xyz.driver.server.controllers.TrialController.resolve(id: String)
+ // POST /v1/trial/:id/archive xyz.driver.server.controllers.TrialController.archive(id: String)
+ // POST /v1/trial/:id/unassign xyz.driver.server.controllers.TrialController.unassign(id: String)
+
+ def getById(id: StringId[Trial])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] = {
+ val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/trial/$id"))
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiTrial, GetByIdReply](response) { api =>
+ GetByIdReply.Entity(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def getPdfSource(trialId: StringId[Trial])(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetPdfSourceReply] = Future.failed(
+ new NotImplementedError("Streaming PDF over network is not supported.")
+ )
+
+ def getAll(filter: SearchFilterExpr = SearchFilterExpr.Empty,
+ sorting: Option[Sorting] = None,
+ pagination: Option[Pagination] = None)(
+ implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = {
+
+ val request = HttpRequest(
+ HttpMethods.GET,
+ endpointUri(baseUri, "/v1/trial", filterQuery(filter) ++ sortingQuery(sorting) ++ paginationQuery(pagination)))
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ListResponse[ApiTrial], GetListReply](response) { api =>
+ GetListReply.EntityList(api.items.map(_.toDomain), api.meta.itemsCount, api.meta.lastUpdate)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def update(origTrial: Trial, draftTrial: Trial)(
+ implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] = {
+ val id = origTrial.id.id
+ for {
+ entity <- Marshal(ApiTrial.fromDomain(draftTrial)).to[RequestEntity]
+ request = HttpRequest(HttpMethods.PATCH, endpointUri(baseUri, s"/v1/trial/$id")).withEntity(entity)
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiTrial, UpdateReply](response) { api =>
+ UpdateReply.Updated(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ private def singleAction(origTrial: Trial, action: String)(
+ implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] = {
+ val id = origTrial.id.id
+ val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/trial/$id/$action"))
+ for {
+ response <- transport.sendRequestGetResponse(requestContext)(request)
+ reply <- apiResponse[ApiTrial, UpdateReply](response) { api =>
+ UpdateReply.Updated(api.toDomain)
+ }()
+ } yield {
+ reply
+ }
+ }
+
+ def start(origTrial: Trial)(implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] =
+ singleAction(origTrial, "start")
+ def submit(origTrial: Trial)(implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] =
+ singleAction(origTrial, "submit")
+ def restart(origTrial: Trial)(implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] =
+ singleAction(origTrial, "restart")
+ def flag(origTrial: Trial)(implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] =
+ singleAction(origTrial, "flag")
+ def resolve(origTrial: Trial)(implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] =
+ singleAction(origTrial, "resolve")
+ def archive(origTrial: Trial)(implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] =
+ singleAction(origTrial, "archive")
+ def unassign(origTrial: Trial)(implicit requestContext: AuthenticatedRequestContext): Future[UpdateReply] =
+ singleAction(origTrial, "unassign")
+
+}