From f9ac0adf5c3bcfcde03bd3ea2bc2471b0d0f99fe Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Wed, 5 Jul 2017 19:02:13 -0700 Subject: Implement REST services for trial curation --- .../services/rest/RestMessageService.scala | 103 +++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala (limited to 'src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala') 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 + } + } + +} -- cgit v1.2.3 From 3e700be0b7df8022627b1f46890f3e3dad3fa54b Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Wed, 12 Jul 2017 22:26:07 -0700 Subject: Handle errors by failing futures in REST services --- .../xyz/driver/pdsuicommon/error/DomainError.scala | 8 ++ .../xyz/driver/pdsuicommon/error/ErrorCode.scala | 17 +++++ .../driver/pdsuicommon/error/ErrorsResponse.scala | 89 ++++++++++++++++++++++ .../pdsuidomain/services/rest/RestArmService.scala | 10 +-- .../services/rest/RestCriterionService.scala | 10 +-- .../pdsuidomain/services/rest/RestHelper.scala | 47 +++++------- .../services/rest/RestHypothesisService.scala | 2 +- .../services/rest/RestInterventionService.scala | 6 +- .../rest/RestInterventionTypeService.scala | 7 +- .../services/rest/RestMessageService.scala | 8 +- .../services/rest/RestStudyDesignService.scala | 2 +- .../services/rest/RestTrialService.scala | 8 +- 12 files changed, 159 insertions(+), 55 deletions(-) create mode 100644 src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala create mode 100644 src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala (limited to 'src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala') diff --git a/src/main/scala/xyz/driver/pdsuicommon/error/DomainError.scala b/src/main/scala/xyz/driver/pdsuicommon/error/DomainError.scala index 4bf90d1..fc8e474 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/error/DomainError.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/error/DomainError.scala @@ -28,3 +28,11 @@ object DomainError { Unsafe(Utils.getClassSimpleName(x.getClass)) } } + +/** Subclasses of this exception correspond to subclasses of DomainError. They + * are used in REST service implementations to fail futures rather than + * returning successful futures, completed with corresponding DomainErrors. */ +class DomainException(message: String) extends RuntimeException(message) +class NotFoundException(message: String) extends DomainException(message) // 404 +class AuthenticationException(message: String) extends DomainException(message) // 401 +class AuthorizationException(message: String) extends DomainException(message) // 403 diff --git a/src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala b/src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala new file mode 100644 index 0000000..5574c01 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala @@ -0,0 +1,17 @@ +package xyz.driver.pdsuicommon.error + +import play.api.libs.functional.syntax._ +import play.api.libs.json.{Format, Reads, Writes} + +@SuppressWarnings(Array("org.wartremover.warts.Enumeration")) +object ErrorCode extends Enumeration { + + type ErrorCode = Value + val Unspecified = Value(1) + + private val fromJsonReads: Reads[ErrorCode] = Reads.of[Int].map(ErrorCode.apply) + private val toJsonWrites: Writes[ErrorCode] = Writes.of[Int].contramap(_.id) + + implicit val jsonFormat: Format[ErrorCode] = Format(fromJsonReads, toJsonWrites) + +} diff --git a/src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala b/src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala new file mode 100644 index 0000000..6ceadb2 --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala @@ -0,0 +1,89 @@ +package xyz.driver.pdsuicommon.error + +import xyz.driver.pdsuicommon.json.Serialization.seqJsonFormat +import ErrorCode.{ErrorCode, Unspecified} +import ErrorsResponse.ResponseError +import xyz.driver.pdsuicommon.auth.{AnonymousRequestContext, RequestId} +import xyz.driver.pdsuicommon.utils.Utils +import play.api.http.Writeable +import play.api.libs.functional.syntax._ +import play.api.libs.json._ +import play.api.mvc.{Result, Results} +import xyz.driver.pdsuicommon.validation.JsonValidationErrors + +class ErrorsResponse(val errors: Seq[ResponseError], private val httpStatus: Results#Status, val requestId: RequestId) { + def toResult(implicit writeable: Writeable[ErrorsResponse]): Result = httpStatus(this) +} + +object ErrorsResponse { + + /** + * @param data Any data that can be associated with particular error.Ex.: error field name + * @param message Error message + * @param code Unique error code + * + * @see https://driverinc.atlassian.net/wiki/display/RA/REST+API+Specification#RESTAPISpecification-HTTPStatuscodes + */ + final case class ResponseError(data: Option[String], message: String, code: ErrorCode) + + object ResponseError { + + implicit val responseErrorJsonFormat: Format[ResponseError] = ( + (JsPath \ "data").formatNullable[String] and + (JsPath \ "message").format[String] and + (JsPath \ "code").format[ErrorCode] + )(ResponseError.apply, unlift(ResponseError.unapply)) + + } + + implicit val writes: Writes[ErrorsResponse] = Writes { errorsResponse => + Json.obj( + "errors" -> Json.toJson(errorsResponse.errors), + "requestId" -> Json.toJson(errorsResponse.requestId.value) + ) + } + + // deprecated, will be removed in REP-436 + def fromString(message: String, httpStatus: Results#Status)( + implicit context: AnonymousRequestContext): ErrorsResponse = { + new ErrorsResponse( + errors = Seq( + ResponseError( + data = None, + message = message, + code = Unspecified + )), + httpStatus = httpStatus, + requestId = context.requestId + ) + } + + // scalastyle:off null + def fromExceptionMessage(e: Throwable, httpStatus: Results#Status = Results.InternalServerError)( + implicit context: AnonymousRequestContext): ErrorsResponse = { + val message = if (e.getMessage == null || e.getMessage.isEmpty) { + Utils.getClassSimpleName(e.getClass) + } else { + e.getMessage + } + + fromString(message, httpStatus) + } + // scalastyle:on null + + // deprecated, will be removed in REP-436 + def fromJsonValidationErrors(validationErrors: JsonValidationErrors)( + implicit context: AnonymousRequestContext): ErrorsResponse = { + val errors = validationErrors.map { + case (path, xs) => + ResponseError( + data = Some(path.toString()), + message = xs.map(_.message).mkString("\n"), + code = Unspecified + ) + } + + new ErrorsResponse(errors, Results.BadRequest, context.requestId) + } + +} diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala index 299e6f8..e7cd2db 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala @@ -40,7 +40,7 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ListResponse[ApiArm], GetListReply](response) { api => GetListReply.EntityList(api.items.map(_.toDomain), api.meta.itemsCount) - }() + } } yield { reply } @@ -52,7 +52,7 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiArm, GetByIdReply](response) { api => GetByIdReply.Entity(api.toDomain) - }() + } } yield { reply } @@ -65,7 +65,7 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiArm, CreateReply](response) { api => CreateReply.Created(api.toDomain) - }() + } } yield { reply } @@ -78,7 +78,7 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiArm, UpdateReply](response) { api => UpdateReply.Updated(api.toDomain) - }() + } } yield { reply } @@ -90,7 +90,7 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect 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 index b27ce3d..f2cc9a8 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala @@ -36,7 +36,7 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiCriterion, CreateReply](response) { api => CreateReply.Created(api.toDomain) - }() + } } yield { reply } @@ -48,7 +48,7 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiCriterion, GetByIdReply](response) { api => GetByIdReply.Entity(api.toDomain) - }() + } } yield { reply } @@ -72,7 +72,7 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( api.meta.itemsCount, api.meta.lastUpdate ) - }() + } } yield { reply } @@ -87,7 +87,7 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiCriterion, UpdateReply](response) { api => UpdateReply.Updated(api.toDomain) - }() + } } yield { reply } @@ -99,7 +99,7 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( 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 index 7d2838b..bd9580a 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala @@ -1,10 +1,8 @@ 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.model.{HttpResponse, ResponseEntity, StatusCodes, Uri} import akka.http.scaladsl.unmarshalling.{Unmarshal, Unmarshaller} import akka.stream.Materializer import xyz.driver.core.rest.ServiceRequestContext @@ -17,7 +15,8 @@ import xyz.driver.pdsuicommon.db.{ Sorting, SortingOrder } -import xyz.driver.pdsuicommon.error.DomainError +import xyz.driver.pdsuicommon.serialization.PlayJsonSupport +import xyz.driver.pdsuicommon.error._ trait RestHelper { @@ -30,9 +29,6 @@ trait RestHelper { 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 { @@ -97,33 +93,30 @@ trait RestHelper { * @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 - ) + def extractErrorMessage(response: HttpResponse): Future[String] = { + import PlayJsonSupport._ + Unmarshal(response.entity) + .to[ErrorsResponse.ResponseError] + .transform( + _.message, + ex => new DomainException(ex.getMessage) + ) + } 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}") - ) + extractErrorMessage(response).flatMap { message => + Future.failed(response.status match { + case StatusCodes.Unauthorized => new AuthenticationException(message) + case StatusCodes.Forbidden => new AuthorizationException(message) + case StatusCodes.NotFound => new NotFoundException(message) + case other => + new DomainException(s"Unhandled domain error for HTTP status ${other.value}. Message ${message}") + }) } } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala index ff9d490..085c347 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala @@ -30,7 +30,7 @@ class RestHypothesisService(transport: ServiceTransport, baseUri: Uri)( 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 index 810a9d6..9b5c51c 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala @@ -40,7 +40,7 @@ class RestInterventionService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ListResponse[ApiIntervention], GetListReply](response) { api => GetListReply.EntityList(api.items.map(_.toDomain), api.meta.itemsCount) - }() + } } yield { reply } @@ -53,7 +53,7 @@ class RestInterventionService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiIntervention, GetByIdReply](response) { api => GetByIdReply.Entity(api.toDomain) - }() + } } yield { reply } @@ -69,7 +69,7 @@ class RestInterventionService(transport: ServiceTransport, baseUri: Uri)( 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 index 1243500..8a4694c 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala @@ -7,7 +7,6 @@ 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 @@ -23,14 +22,12 @@ class RestInterventionTypeService(transport: ServiceTransport, baseUri: Uri)( def getAll(sorting: Option[Sorting] = None)( implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = { + val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, "/v1/intervention-type", sortingQuery(sorting))) for { - response <- transport.sendRequestGetResponse(requestContext)( - get(baseUri, "/v1/intervention-type", query = sortingQuery(sorting))) + response <- transport.sendRequestGetResponse(requestContext)(request) 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 index 61d2050..e0e0813 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala @@ -34,7 +34,7 @@ class RestMessageService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiMessage, CreateReply](response) { api => CreateReply.Created(api.toDomain) - }() + } } yield { reply } @@ -66,7 +66,7 @@ class RestMessageService(transport: ServiceTransport, baseUri: Uri)( 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 } @@ -81,7 +81,7 @@ class RestMessageService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiMessage, UpdateReply](response) { api => UpdateReply.Updated(api.toDomain) - }() + } } yield { reply } @@ -94,7 +94,7 @@ class RestMessageService(transport: ServiceTransport, baseUri: Uri)( 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 index 66f7a78..1945b0a 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala @@ -28,7 +28,7 @@ class RestStudyDesignService(transport: ServiceTransport, baseUri: Uri)( 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 index 9b4b576..db7f79e 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala @@ -41,7 +41,7 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiTrial, GetByIdReply](response) { api => GetByIdReply.Entity(api.toDomain) - }() + } } yield { reply } @@ -64,7 +64,7 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)( 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 } @@ -79,7 +79,7 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiTrial, UpdateReply](response) { api => UpdateReply.Updated(api.toDomain) - }() + } } yield { reply } @@ -93,7 +93,7 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)( response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiTrial, UpdateReply](response) { api => UpdateReply.Updated(api.toDomain) - }() + } } yield { reply } -- cgit v1.2.3 From e5fe8c9a46426ff19757b37a8bcb2b3f51fc2701 Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Thu, 13 Jul 2017 02:06:27 -0700 Subject: Refactor reply mapping --- .../pdsuidomain/services/rest/RestArmService.scala | 42 ++++++-------------- .../services/rest/RestCriterionService.scala | 42 +++++--------------- .../pdsuidomain/services/rest/RestHelper.scala | 10 ++--- .../services/rest/RestHypothesisService.scala | 10 +---- .../services/rest/RestInterventionService.scala | 30 ++++---------- .../rest/RestInterventionTypeService.scala | 11 +++--- .../services/rest/RestMessageService.scala | 43 +++++++------------- .../services/rest/RestStudyDesignService.scala | 8 +--- .../services/rest/RestTrialService.scala | 46 ++++++---------------- 9 files changed, 69 insertions(+), 173 deletions(-) (limited to 'src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala') diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala index e7cd2db..0bbbb72 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala @@ -1,8 +1,8 @@ package xyz.driver.pdsuidomain.services.rest -import akka.http.scaladsl.marshalling.Marshal 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._ @@ -18,31 +18,21 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect 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) + import xyz.driver.pdsuidomain.services.ArmService._ 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) - } + reply <- apiResponse[ListResponse[ApiArm]](response) } yield { - reply + GetListReply.EntityList(reply.items.map(_.toDomain), reply.meta.itemsCount) } } @@ -50,11 +40,9 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect 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) - } + reply <- apiResponse[ApiArm](response) } yield { - reply + GetByIdReply.Entity(reply.toDomain) } } @@ -63,11 +51,9 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect 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) - } + reply <- apiResponse[ApiArm](response) } yield { - reply + CreateReply.Created(reply.toDomain) } } @@ -76,11 +62,9 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect 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) - } + reply <- apiResponse[ApiArm](response) } yield { - reply + UpdateReply.Updated(reply.toDomain) } } @@ -88,11 +72,9 @@ class RestArmService(transport: ServiceTransport, baseUri: Uri)(implicit protect val request = HttpRequest(HttpMethods.DELETE, endpointUri(baseUri, s"/v1/arm/$id")) for { response <- transport.sendRequestGetResponse(requestContext)(request) - reply <- apiResponse[ApiArm, DeleteReply](response) { _ => - DeleteReply.Deleted - } + reply <- apiResponse[ApiArm](response) } yield { - reply + DeleteReply.Deleted } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala index f2cc9a8..2ec4db0 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala @@ -22,23 +22,15 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ApiCriterion](response) } yield { - reply + CreateReply.Created(reply.toDomain) } } @@ -46,11 +38,9 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ApiCriterion](response) } yield { - reply + GetByIdReply.Entity(reply.toDomain) } } @@ -58,23 +48,15 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( 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 - ) - } + reply <- apiResponse[ListResponse[ApiCriterion]](response) } yield { - reply + GetListReply.EntityList(reply.items.map(_.toDomain), reply.meta.itemsCount, reply.meta.lastUpdate) } } @@ -85,11 +67,9 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ApiCriterion](response) } yield { - reply + UpdateReply.Updated(reply.toDomain) } } @@ -97,11 +77,9 @@ class RestCriterionService(transport: ServiceTransport, baseUri: Uri)( val request = HttpRequest(HttpMethods.DELETE, endpointUri(baseUri, s"/v1/criterion/$id")) for { response <- transport.sendRequestGetResponse(requestContext)(request) - reply <- apiResponse[ApiCriterion, DeleteReply](response) { _ => - DeleteReply.Deleted - } + reply <- apiResponse[ApiCriterion](response) } yield { - reply + DeleteReply.Deleted } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala index bd9580a..3113b21 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala @@ -15,7 +15,6 @@ import xyz.driver.pdsuicommon.db.{ Sorting, SortingOrder } -import xyz.driver.pdsuicommon.serialization.PlayJsonSupport import xyz.driver.pdsuicommon.error._ trait RestHelper { @@ -92,11 +91,11 @@ trait RestHelper { * 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)( - implicit unmarshaller: Unmarshaller[ResponseEntity, ApiReply]): Future[DomainReply] = { + def apiResponse[ApiReply](response: HttpResponse)( + implicit unmarshaller: Unmarshaller[ResponseEntity, ApiReply]): Future[ApiReply] = { def extractErrorMessage(response: HttpResponse): Future[String] = { - import PlayJsonSupport._ + import xyz.driver.pdsuicommon.serialization.PlayJsonSupport._ Unmarshal(response.entity) .to[ErrorsResponse.ResponseError] .transform( @@ -106,8 +105,7 @@ trait RestHelper { } if (response.status.isSuccess) { - val reply = Unmarshal(response.entity).to[ApiReply] - reply.map(successMapper) + Unmarshal(response.entity).to[ApiReply] } else { extractErrorMessage(response).flatMap { message => Future.failed(response.status match { diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala index 085c347..37b28ae 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHypothesisService.scala @@ -19,20 +19,14 @@ class RestHypothesisService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ListResponse[ApiHypothesis]](response) } yield { - reply + GetListReply.EntityList(reply.items.map(_.toDomain), reply.meta.itemsCount) } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala index 9b5c51c..19509a3 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala @@ -1,8 +1,8 @@ package xyz.driver.pdsuidomain.services.rest -import akka.http.scaladsl.marshalling.Marshal 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._ @@ -19,59 +19,45 @@ class RestInterventionService(transport: ServiceTransport, baseUri: Uri)( 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) + import xyz.driver.pdsuidomain.services.InterventionService._ 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) - } + reply <- apiResponse[ListResponse[ApiIntervention]](response) } yield { - reply + GetListReply.EntityList(reply.items.map(_.toDomain), reply.meta.itemsCount) } } 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) - } + reply <- apiResponse[ApiIntervention](response) } yield { - reply + GetByIdReply.Entity(reply.toDomain) } } 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) - } + reply <- apiResponse[ApiIntervention](response) } yield { - reply + UpdateReply.Updated(reply.toDomain) } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala index 8a4694c..fad2841 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionTypeService.scala @@ -21,16 +21,15 @@ class RestInterventionTypeService(transport: ServiceTransport, baseUri: Uri)( def getAll(sorting: Option[Sorting] = None)( implicit requestContext: AuthenticatedRequestContext): Future[GetListReply] = { - val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, "/v1/intervention-type", sortingQuery(sorting))) for { response <- transport.sendRequestGetResponse(requestContext)(request) - reply <- apiResponse[ListResponse[ApiInterventionType], GetListReply](response) { list => - val domain = list.items.map(_.toDomain) - GetListReply.EntityList(domain.toList, list.meta.itemsCount) - } + reply <- apiResponse[ListResponse[ApiInterventionType]](response) } yield { - reply + { + val domain = reply.items.map(_.toDomain) + GetListReply.EntityList(domain.toList, reply.meta.itemsCount) + } } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala index e0e0813..17ded0a 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala @@ -1,8 +1,8 @@ package xyz.driver.pdsuidomain.services.rest -import akka.http.scaladsl.marshalling.Marshal 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._ @@ -22,53 +22,43 @@ class RestMessageService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ApiMessage](response) } yield { - reply + CreateReply.Created(reply.toDomain) } } def getById(messageId: LongId[Message])(implicit requestContext: AuthenticatedRequestContext): Future[GetByIdReply] = { - import SearchFilterExpr._ - import SearchFilterBinaryOperation._ + import xyz.driver.pdsuicommon.db.SearchFilterBinaryOperation._ + import xyz.driver.pdsuicommon.db.SearchFilterExpr._ val filter = Atom.Binary("id", Eq, messageId) - getAll(filter).map { + 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) - } + reply <- apiResponse[ListResponse[ApiMessage]](response) } yield { - reply + GetListReply.EntityList(reply.items.map(_.toDomain), reply.meta.itemsCount, reply.meta.lastUpdate) } } @@ -79,24 +69,19 @@ class RestMessageService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ApiMessage](response) } yield { - reply + UpdateReply.Updated(reply.toDomain) } } 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 - } + reply <- apiResponse[ApiMessage](response) } yield { - reply + DeleteReply.Deleted } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala index 1945b0a..4fba287 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestStudyDesignService.scala @@ -19,18 +19,14 @@ class RestStudyDesignService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ListResponse[ApiStudyDesign]](response) } yield { - reply + GetListReply.EntityList(reply.items.map(_.toDomain), reply.meta.itemsCount) } } 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 db7f79e..9265280 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala @@ -1,9 +1,9 @@ package xyz.driver.pdsuidomain.services.rest -import akka.http.scaladsl.marshalling.Marshal import scala.NotImplementedError 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._ @@ -11,9 +11,9 @@ 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.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, @@ -23,50 +23,32 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ApiTrial](response) } yield { - reply + GetByIdReply.Entity(reply.toDomain) } } def getPdfSource(trialId: StringId[Trial])( - implicit requestContext: AuthenticatedRequestContext): Future[GetPdfSourceReply] = Future.failed( - new NotImplementedError("Streaming PDF over network is not supported.") - ) + 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) - } + reply <- apiResponse[ListResponse[ApiTrial]](response) } yield { - reply + GetListReply.EntityList(reply.items.map(_.toDomain), reply.meta.itemsCount, reply.meta.lastUpdate) } } @@ -77,11 +59,9 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ApiTrial](response) } yield { - reply + UpdateReply.Updated(reply.toDomain) } } @@ -91,11 +71,9 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)( 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) - } + reply <- apiResponse[ApiTrial](response) } yield { - reply + UpdateReply.Updated(reply.toDomain) } } -- cgit v1.2.3 From 7fe13f4d62b8865db7434383085d960d9d23ddec Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Thu, 13 Jul 2017 12:36:34 -0700 Subject: Upgrade domain model and core --- build.sbt | 4 ++-- .../scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala | 2 +- .../xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala | 2 +- .../driver/pdsuidomain/services/rest/RestInterventionService.scala | 2 +- .../xyz/driver/pdsuidomain/services/rest/RestMessageService.scala | 2 +- .../scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala') diff --git a/build.sbt b/build.sbt index 82a3249..715dbc3 100644 --- a/build.sbt +++ b/build.sbt @@ -17,8 +17,8 @@ lazy val core = (project in file(".")) "io.github.cloudify" %% "spdf" % "1.4.0", "org.davidbild" %% "tristate-core" % "0.2.0", "org.davidbild" %% "tristate-play" % "0.2.0" exclude ("com.typesafe.play", "play-json"), - "xyz.driver" %% "core" % "0.13.15", - "xyz.driver" %% "domain-model" % "0.10.40", + "xyz.driver" %% "core" % "0.13.22", + "xyz.driver" %% "domain-model" % "0.11.5", "ch.qos.logback" % "logback-classic" % "1.1.7", "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.8.4", "com.github.spullara.mustache.java" % "scala-extensions-2.11" % "0.9.4", diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala index 0bbbb72..0a29c08 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestArmService.scala @@ -5,7 +5,7 @@ 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.core.rest.{Pagination => _, _} import xyz.driver.pdsuicommon.auth._ import xyz.driver.pdsuicommon.db._ import xyz.driver.pdsuicommon.domain._ diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala index 2ec4db0..7470de1 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestCriterionService.scala @@ -5,7 +5,7 @@ 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.core.rest.{Pagination => _, _} import xyz.driver.pdsuicommon.auth._ import xyz.driver.pdsuicommon.db._ import xyz.driver.pdsuicommon.domain._ diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala index 19509a3..aa59657 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestInterventionService.scala @@ -5,7 +5,7 @@ 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.core.rest.{Pagination => _, _} import xyz.driver.pdsuicommon.auth._ import xyz.driver.pdsuicommon.db._ import xyz.driver.pdsuicommon.domain._ diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala index 17ded0a..4eb48d5 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala @@ -5,7 +5,7 @@ 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.core.rest.{Pagination => _, _} import xyz.driver.pdsuicommon.auth._ import xyz.driver.pdsuicommon.db._ import xyz.driver.pdsuicommon.domain._ 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 9265280..e1f9d87 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala @@ -6,7 +6,7 @@ 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.core.rest.{Pagination => _, _} import xyz.driver.pdsuicommon.auth._ import xyz.driver.pdsuicommon.db._ import xyz.driver.pdsuicommon.domain._ -- cgit v1.2.3 From d733294cb16fa83be014ce1efccbf364aa309d25 Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Fri, 14 Jul 2017 15:05:12 -0700 Subject: Fix PR --- src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala | 9 +++++---- .../driver/pdsuidomain/formats/json/trial/ApiTrial.scala | 6 +++++- .../xyz/driver/pdsuidomain/services/rest/RestHelper.scala | 13 ++++--------- .../pdsuidomain/services/rest/RestMessageService.scala | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala') diff --git a/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala b/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala index f5c6974..2f90820 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/entities/Trial.scala @@ -70,10 +70,11 @@ object Trial { case object Lung extends Condition case object Prostate extends Condition - def fromString(condition: String): Condition = condition match { - case "Breast" => Breast - case "Lung" => Lung - case "Prostate" => Prostate + def fromString(condition: String): Option[Condition] = condition match { + case "Breast" => Some(Breast) + case "Lung" => Some(Lung) + case "Prostate" => Some(Prostate) + case _ => None } val All: Set[Condition] = Set(Breast, Lung, Prostate) 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 2556feb..f59836e 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 @@ -37,7 +37,11 @@ final case class ApiTrial(id: String, previousAssignee = this.previousAssignee.map(id => StringId(id)), lastActiveUserId = this.lastActiveUser.map(id => StringId(id)), lastUpdate = this.lastUpdate.toLocalDateTime(), - condition = Trial.Condition.fromString(this.condition), + condition = Trial.Condition + .fromString(this.condition) + .getOrElse( + throw new NoSuchElementException(s"unknown condition ${this.condition}") + ), phase = this.phase, hypothesisId = this.hypothesisId.map(id => UuidId(id)), studyDesignId = this.studyDesignId.map(id => LongId(id)), diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala index 3113b21..5284ff1 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala @@ -74,21 +74,16 @@ trait RestHelper { case Some(pp) => Seq( "pageNumber" -> pp.pageNumber.toString, - "pageSize" -> pp.pageSize.toHexString + "pageSize" -> pp.pageSize.toString ) } - /** Utility method to parse responses that encode success and errors as subtypes - * of a common reply type. + /** Utility method to parse responses from records-acquisition-server. * + * Non-2xx HTTP error codes will be cause the returned future to fail with a corresponding + * `DomainException`. * @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](response: HttpResponse)( diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala index 4eb48d5..1527ad5 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestMessageService.scala @@ -76,7 +76,7 @@ class RestMessageService(transport: ServiceTransport, baseUri: Uri)( } def delete(messageId: LongId[Message])(implicit requestContext: AuthenticatedRequestContext): Future[DeleteReply] = { - val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/message/${messageId.id}")) + val request = HttpRequest(HttpMethods.DELETE, endpointUri(baseUri, s"/v1/message/${messageId.id}")) for { response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[ApiMessage](response) -- cgit v1.2.3