aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/pdsuicommon/error
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/pdsuicommon/error')
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/error/DomainError.scala8
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/error/ErrorCode.scala17
-rw-r--r--src/main/scala/xyz/driver/pdsuicommon/error/ErrorsResponse.scala89
3 files changed, 114 insertions, 0 deletions
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)
+ }
+
+}