diff options
author | Kseniya Tomskikh <ktomskih@datamonsters.co> | 2018-01-10 12:09:10 +0700 |
---|---|---|
committer | Kseniya Tomskikh <ktomskih@datamonsters.co> | 2018-01-10 12:09:10 +0700 |
commit | 7d9c3a7a0de129eef06d5b2eac92aafec1229098 (patch) | |
tree | 74c839223186e73c017b2e07a5cd43e9754498e5 /src/main | |
parent | 2456ac8336df69734890b325b0b2a5a1182e93e2 (diff) | |
parent | 5bbb0bc66db5b49eb0e3f197be17798fb8093f2a (diff) | |
download | rest-query-7d9c3a7a0de129eef06d5b2eac92aafec1229098.tar.gz rest-query-7d9c3a7a0de129eef06d5b2eac92aafec1229098.tar.bz2 rest-query-7d9c3a7a0de129eef06d5b2eac92aafec1229098.zip |
Merge branch 'master' into PDSUI-2432
Diffstat (limited to 'src/main')
8 files changed, 202 insertions, 58 deletions
diff --git a/src/main/scala/xyz/driver/pdsuicommon/error/DomainError.scala b/src/main/scala/xyz/driver/pdsuicommon/error/DomainError.scala index c761414..becb585 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/error/DomainError.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/error/DomainError.scala @@ -16,9 +16,6 @@ object DomainError { // 404 error trait NotFoundError extends DomainError - // 401 error - trait AuthenticationError extends DomainError - // 403 error trait AuthorizationError extends DomainError @@ -28,14 +25,3 @@ 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. */ -// scalastyle:off null -@SuppressWarnings(Array("org.wartremover.warts.Null")) -class DomainException(message: String, cause: Throwable = null) extends RuntimeException(message, cause) -class NotFoundException(message: String) extends DomainException(message) // 404 -class AuthenticationException(message: String) extends DomainException(message) // 401 -class AuthorizationException(message: String) extends DomainException(message) // 403 -// scalastyle:on null diff --git a/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala b/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala index 4f53a9d..46b86a6 100644 --- a/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala +++ b/src/main/scala/xyz/driver/pdsuicommon/http/Directives.scala @@ -62,10 +62,9 @@ trait Directives { PathMatchers.JavaUUID.map((id) => UuidId(id)) def failFast[A](reply: A): A = reply match { - case err: NotFoundError => throw new NotFoundException(err.getMessage) - case err: AuthenticationError => throw new AuthenticationException(err.getMessage) - case err: AuthorizationError => throw new AuthorizationException(err.getMessage) - case err: DomainError => throw new DomainException(err.getMessage) + case err: NotFoundError => throw ResourceNotFoundException(err.getMessage) + case err: AuthorizationError => throw InvalidActionException(err.getMessage) + case err: DomainError => throw InvalidInputException(err.getMessage) case other => other } @@ -73,27 +72,15 @@ trait Directives { def errorResponse(msg: String, code: Int) = ErrorsResponse(Seq(ResponseError(None, msg, code)), req) ExceptionHandler { - case ex: AuthenticationException => - complete(StatusCodes.Unauthorized -> errorResponse(ex.getMessage, 401)) - case ex: InvalidActionException => complete(StatusCodes.Forbidden -> errorResponse(ex.message, 403)) - case ex: AuthorizationException => - complete(StatusCodes.Forbidden -> errorResponse(ex.getMessage, 403)) - case ex: ResourceNotFoundException => complete(StatusCodes.NotFound -> errorResponse(ex.message, 404)) - case ex: NotFoundException => - complete(StatusCodes.NotFound -> errorResponse(ex.getMessage, 404)) - case ex: InvalidInputException => complete(StatusCodes.BadRequest -> errorResponse(ex.message, 400)) - case ex: DomainException => - complete(StatusCodes.BadRequest -> errorResponse(ex.getMessage, 400)) - case NonFatal(ex) => complete(StatusCodes.InternalServerError -> errorResponse(ex.getMessage, 500)) } diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/documentissue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/documentissue.scala index d17a45c..037a3ad 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/documentissue.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/documentissue.scala @@ -24,14 +24,14 @@ object documentissue { .map(_.convertTo[Boolean]) .getOrElse(deserializationError(s"DocumentIssue json object does not contain `archiveRequired` field: $json")) - val startPage = fields.get("startPage").map(_.convertTo[Double]) - val endPage = fields.get("endPage").map(_.convertTo[Double]) + val startPage = fields.get("startPage").map(_.convertTo[Option[Double]]) + val endPage = fields.get("endPage").map(_.convertTo[Option[Double]]) orig.copy( text = text, archiveRequired = archiveRequired, - startPage = startPage, - endPage = endPage + startPage = startPage.getOrElse(orig.startPage), + endPage = endPage.getOrElse(orig.endPage) ) case _ => deserializationError(s"Expected Json Object as partial DocumentIssue, but got $json") @@ -49,8 +49,8 @@ object documentissue { .getOrElse(deserializationError(s"DocumentIssue json object does not contain `text` field: $json")) val id = fields.get("id").map(_.convertTo[LongId[DocumentIssue]]) - val startPage = fields.get("startPage").map(_.convertTo[Double]) - val endPage = fields.get("endPage").map(_.convertTo[Double]) + val startPage = fields.get("startPage").flatMap(_.convertTo[Option[Double]]) + val endPage = fields.get("endPage").flatMap(_.convertTo[Option[Double]]) DocumentIssue( id = id.getOrElse(LongId(0)), diff --git a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue.scala b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue.scala index f9bb0b6..79882c2 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/formats/json/trialissue.scala @@ -2,16 +2,26 @@ package xyz.driver.pdsuidomain.formats.json import java.time.LocalDateTime -import spray.json._ +import spray.json.{RootJsonReader, _} +import xyz.driver.core.{Id, auth} import xyz.driver.core.auth.User import xyz.driver.core.json._ import xyz.driver.pdsuicommon.domain.{LongId, StringId} import xyz.driver.pdsuidomain.entities._ object trialissue { + import DefaultJsonProtocol._ import common._ + private def deserializationErrorFieldMessage(field: String, json: JsValue)(implicit className: String) = { + deserializationError(s"$className json object do not contain '$field' field: $json") + } + + private def deserializationErrorEntityMessage(json: JsValue)(implicit className: String) = { + deserializationError(s"Expected Json Object as $className, but got $json") + } + def applyUpdateToTrialIssue(json: JsValue, orig: TrialIssue): TrialIssue = { json.asJsObject.getFields("text", "evidence", "archiveRequired", "meta") match { case Seq(text, evidence, archiveRequired, meta) => @@ -48,15 +58,77 @@ object trialissue { implicit val trialIssueWriter = new RootJsonWriter[TrialIssue] { override def write(obj: TrialIssue) = JsObject( - "id" -> obj.id.toJson, - "text" -> obj.text.toJson, - "lastUpdate" -> obj.lastUpdate.toJson, - "userId" -> obj.userId.toJson, - "isDraft" -> obj.isDraft.toJson, - "evidence" -> obj.evidence.toJson, + "id" -> obj.id.toJson, + "text" -> obj.text.toJson, + "lastUpdate" -> obj.lastUpdate.toJson, + "userId" -> obj.userId.toJson, + "isDraft" -> obj.isDraft.toJson, + "evidence" -> obj.evidence.toJson, "archiveRequired" -> obj.archiveRequired.toJson, - "meta" -> obj.meta.toJson + "meta" -> obj.meta.toJson ) } + def trialIssueReader(trialId: StringId[Trial]): RootJsonReader[TrialIssue] = + new RootJsonReader[TrialIssue] { + implicit val className: String = "TrialIssue" + + override def read(json: JsValue): TrialIssue = json match { + case JsObject(fields) => + val id = fields + .get("id") + .map(_.convertTo[LongId[TrialIssue]]) + .getOrElse(deserializationErrorFieldMessage("id", json)) + + val text = fields + .get("text") + .map(_.convertTo[String]) + .getOrElse(deserializationErrorFieldMessage("text", json)) + + val lastUpdate = fields + .get("lastUpdate") + .map(_.convertTo[LocalDateTime]) + .getOrElse(deserializationErrorFieldMessage("lastUpdate", json)) + + val userId = fields + .get("userId") + .map(_.convertTo[Id[auth.User]]) + .getOrElse(deserializationErrorFieldMessage("userId", json)) + + val isDraft = fields + .get("isDraft") + .map(_.convertTo[Boolean]) + .getOrElse(deserializationErrorFieldMessage("isDraft", json)) + + val evidence = fields + .get("evidence") + .map(_.convertTo[String]) + .getOrElse(deserializationErrorFieldMessage("evidence", json)) + + val archiveRequired = fields + .get("archiveRequired") + .map(_.convertTo[Boolean]) + .getOrElse(deserializationErrorFieldMessage("archiveRequired", json)) + + val meta = fields + .get("meta") + .map(_.convertTo[String]) + .getOrElse(deserializationErrorFieldMessage("meta", json)) + + TrialIssue( + id = id, + userId = userId, + trialId = trialId, + lastUpdate = lastUpdate, + isDraft = isDraft, + text = text, + evidence = evidence, + archiveRequired = archiveRequired, + meta = meta + ) + + case _ => deserializationErrorEntityMessage(json) + } + } + } diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/EligibilityArmService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/EligibilityArmService.scala index 300091e..8e67627 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/EligibilityArmService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/EligibilityArmService.scala @@ -125,6 +125,9 @@ trait EligibilityArmService { def getByEligibilityId(armId: LongId[EligibilityArm])( implicit requestContext: AuthorizedServiceRequestContext[AuthUserInfo]): Future[GetByIdReply] + def deleteByEligibilityIdSlotArmId(armId: LongId[EligibilityArm], slotArmId: LongId[SlotArm])( + implicit requestContext: AuthorizedServiceRequestContext[AuthUserInfo]): Future[DeleteReply] + def getBySlotId(armId: LongId[SlotArm], filter: SearchFilterExpr = SearchFilterExpr.Empty, sorting: Option[Sorting] = None, 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 527ae73..8ed2651 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestHelper.scala @@ -4,14 +4,8 @@ import scala.concurrent.{ExecutionContext, Future} import akka.http.scaladsl.model.{HttpResponse, ResponseEntity, StatusCodes, Uri} import akka.http.scaladsl.unmarshalling.{Unmarshal, Unmarshaller} import akka.stream.Materializer -import xyz.driver.pdsuicommon.db.{ - Pagination, - SearchFilterBinaryOperation, - SearchFilterExpr, - SearchFilterNAryOperation, - Sorting, - SortingOrder -} +import xyz.driver.core.rest.errors.{InvalidActionException, InvalidInputException, ResourceNotFoundException} +import xyz.driver.pdsuicommon.db.{Pagination, SearchFilterBinaryOperation, SearchFilterExpr, SearchFilterNAryOperation, Sorting, SortingOrder} import xyz.driver.pdsuicommon.error._ trait RestHelper { @@ -93,7 +87,7 @@ trait RestHelper { .to[ErrorsResponse] .transform( response => response.errors.map(_.message).mkString(", "), - ex => new DomainException("Response has invalid format", ex) + ex => InvalidInputException(s"Response has invalid format: ${ex.getMessage}") ) } @@ -102,11 +96,10 @@ trait RestHelper { } else { 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 StatusCodes.Forbidden => InvalidActionException(message) + case StatusCodes.NotFound => ResourceNotFoundException(message) case other => - new DomainException(s"Unhandled domain error for HTTP status ${other.value}. $message") + InvalidInputException(s"Unhandled domain error for HTTP status ${other.value}. $message") }) } } diff --git a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialIssueService.scala b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialIssueService.scala new file mode 100644 index 0000000..b14d35b --- /dev/null +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialIssueService.scala @@ -0,0 +1,103 @@ +package xyz.driver.pdsuidomain.services.rest + +import akka.http.scaladsl.marshalling.Marshal +import akka.http.scaladsl.model._ +import akka.stream.Materializer +import spray.json.RootJsonReader +import xyz.driver.core.rest.{AuthorizedServiceRequestContext, ServiceTransport} +import xyz.driver.entities.users +import xyz.driver.pdsuicommon.db.{Pagination, SearchFilterExpr, Sorting} +import xyz.driver.pdsuicommon.domain.{LongId, StringId} +import xyz.driver.pdsuidomain.ListResponse +import xyz.driver.pdsuidomain.entities.{Trial, TrialIssue} +import xyz.driver.pdsuidomain.services.TrialIssueService + +import scala.concurrent.ExecutionContext + +class RestTrialIssueService(transport: ServiceTransport, baseUri: Uri) + (implicit + protected val materializer: Materializer, + protected val exec: ExecutionContext) + extends TrialIssueService with RestHelper{ + + import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ + import xyz.driver.pdsuidomain.formats.json.listresponse._ + import xyz.driver.pdsuidomain.formats.json.trialissue._ + import xyz.driver.pdsuidomain.services.TrialIssueService._ + + override def create(draft: TrialIssue) + (implicit requestContext: AuthorizedServiceRequestContext[users.AuthUserInfo]) = { + val trialId = draft.trialId + + implicit val jsonReader: RootJsonReader[TrialIssue] = trialIssueReader(trialId) + + for { + entity <- Marshal(draft).to[RequestEntity] + request = HttpRequest(HttpMethods.POST, endpointUri(baseUri, s"/v1/trial/$trialId/issue")).withEntity(entity) + response <- transport.sendRequestGetResponse(requestContext)(request) + reply <- apiResponse[TrialIssue](response) + } yield { + CreateReply.Created(reply) + } + } + + override def getListByTrialId(trialId: StringId[Trial], + filter: SearchFilterExpr, + sorting: Option[Sorting], + pagination: Option[Pagination]) + (implicit requestContext: AuthorizedServiceRequestContext[users.AuthUserInfo]) = { + implicit val jsonReader: RootJsonReader[TrialIssue] = trialIssueReader(trialId) + + val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/trial/$trialId/issue", + filterQuery(filter) ++ sortingQuery(sorting) ++ paginationQuery(pagination)) + ) + for { + response <- transport.sendRequestGetResponse(requestContext)(request) + reply <- apiResponse[ListResponse[TrialIssue]](response) + } yield { + GetListByTrialIdReply.EntityList(reply.items, reply.meta.itemsCount, reply.meta.lastUpdate) + } + } + + override def getById(trialId: StringId[Trial], id: LongId[TrialIssue]) + (implicit requestContext: AuthorizedServiceRequestContext[users.AuthUserInfo]) = { + implicit val jsonReader: RootJsonReader[TrialIssue] = trialIssueReader(trialId) + + val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/trial/$trialId/issue/$id")) + for { + response <- transport.sendRequestGetResponse(requestContext)(request) + reply <- apiResponse[TrialIssue](response) + } yield { + GetByIdReply.Entity(reply) + } + } + + override def update(orig: TrialIssue, draft: TrialIssue) + (implicit requestContext: AuthorizedServiceRequestContext[users.AuthUserInfo]) = { + val trialId = draft.trialId + val id = orig.id.id + + implicit val jsonReader: RootJsonReader[TrialIssue] = trialIssueReader(trialId) + + for { + entity <- Marshal(draft).to[RequestEntity] + request = HttpRequest(HttpMethods.PATCH, endpointUri(baseUri, s"/v1/trial/$trialId/issue/$id")).withEntity(entity) + response <- transport.sendRequestGetResponse(requestContext)(request) + reply <- apiResponse[TrialIssue](response) + } yield { + UpdateReply.Updated(reply) + } + } + + override def delete(trialId: StringId[Trial], id: LongId[TrialIssue]) + (implicit requestContext: AuthorizedServiceRequestContext[users.AuthUserInfo]) = { + val request = HttpRequest(HttpMethods.DELETE, endpointUri(baseUri, s"/v1/trial/$trialId/issue/$id")) + for { + response <- transport.sendRequestGetResponse(requestContext)(request) + _ <- apiResponse[HttpEntity](response) + } yield { + DeleteReply.Deleted + } + } + +} 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 4654c2e..6650d51 100644 --- a/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala +++ b/src/main/scala/xyz/driver/pdsuidomain/services/rest/RestTrialService.scala @@ -110,7 +110,7 @@ class RestTrialService(transport: ServiceTransport, baseUri: Uri)(implicit prote } val id = origTrial.id.id - val request = HttpRequest(HttpMethods.GET, endpointUri(baseUri, s"/v1/trial/$id/$action", query)) + val request = HttpRequest(HttpMethods.POST, endpointUri(baseUri, s"/v1/trial/$id/$action", query)) for { response <- transport.sendRequestGetResponse(requestContext)(request) reply <- apiResponse[Trial](response) |