add errors and docs
This commit is contained in:
parent
00da0e3303
commit
90d0b1d5c7
@ -28,6 +28,8 @@ lazy val root = (project in file("."))
|
|||||||
|
|
||||||
// logs
|
// logs
|
||||||
"org.typelevel" %% "log4cats-slf4j" % "2.6.0",
|
"org.typelevel" %% "log4cats-slf4j" % "2.6.0",
|
||||||
"ch.qos.logback" % "logback-classic" % "1.4.11"
|
"ch.qos.logback" % "logback-classic" % "1.4.11",
|
||||||
|
|
||||||
|
"org.typelevel" %% "vault" % "3.6.0"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,103 +1,52 @@
|
|||||||
package org.yobble.chat_private_service.api.endpoint
|
package org.yobble.chat_private_service.api.endpoint
|
||||||
|
|
||||||
|
import sttp.tapir.*
|
||||||
|
import sttp.tapir.EndpointOutput.*
|
||||||
|
import sttp.tapir.json.circe.*
|
||||||
|
import sttp.tapir.generic.auto.*
|
||||||
|
|
||||||
import io.circe.generic.auto.*
|
import io.circe.generic.auto.*
|
||||||
import sttp.model.StatusCode
|
import sttp.model.StatusCode
|
||||||
import sttp.tapir.*
|
|
||||||
import sttp.tapir.generic.auto.*
|
|
||||||
import sttp.tapir.json.circe.*
|
|
||||||
import sttp.model.headers.WWWAuthenticateChallenge
|
import sttp.model.headers.WWWAuthenticateChallenge
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
import org.yobble.module.response.{BaseResponse, ErrorResponse}
|
import org.yobble.module.response.{BaseResponse, ErrorResponse}
|
||||||
|
import org.yobble.module.response.ErrorResponse._
|
||||||
import org.yobble.module.util.ErrorExamples
|
import org.yobble.module.util.ErrorExamples
|
||||||
|
|
||||||
object TestErrorEndpoint {
|
object TestErrorEndpoint {
|
||||||
|
|
||||||
val testErrorEndpoint: Endpoint[String, Option[Int], (StatusCode, ErrorResponse), BaseResponse, Any] =
|
private def errorVariant(
|
||||||
|
code: StatusCode,
|
||||||
|
example: ErrorResponse,
|
||||||
|
description: String
|
||||||
|
): OneOfVariant[ErrorResponse] = {
|
||||||
|
oneOfVariantValueMatcher[ErrorResponse](
|
||||||
|
code,
|
||||||
|
statusCode(code).and(jsonBody[ErrorResponse].description(description).example(example))
|
||||||
|
) {
|
||||||
|
case resp: ErrorResponse => resp.code == example.code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val testErrorEndpoint: Endpoint[String, Option[Int], ErrorResponse, BaseResponse, Any] =
|
||||||
endpoint.get
|
endpoint.get
|
||||||
.in("test-error")
|
.in("test-error")
|
||||||
.name("Test Error")
|
.name("Test Error")
|
||||||
.securityIn(auth.bearer[String](WWWAuthenticateChallenge.bearer))
|
.securityIn(auth.bearer[String](WWWAuthenticateChallenge.bearer))
|
||||||
.in(query[Option[Int]]("code").description("Optional HTTP status code").example(Some(404)))
|
.in(query[Option[Int]]("code").description("Optional HTTP status code").example(Some(404)))
|
||||||
.errorOut(
|
.errorOut(
|
||||||
oneOf[(StatusCode, ErrorResponse)](
|
oneOf[ErrorResponse](
|
||||||
oneOfVariantValueMatcher(
|
errorVariant(StatusCode.BadRequest, ErrorExamples.badRequest, "Bad Request"),
|
||||||
statusCode.and(
|
errorVariant(StatusCode.Unauthorized, ErrorExamples.unauthorized, "Unauthorized"),
|
||||||
jsonBody[ErrorResponse]
|
errorVariant(StatusCode.Forbidden, ErrorExamples.forbidden, "Forbidden"),
|
||||||
.description("Bad Request")
|
errorVariant(StatusCode.NotFound, ErrorExamples.notFound, "Not Found"),
|
||||||
.example(ErrorExamples.badRequest)
|
errorVariant(StatusCode.MethodNotAllowed, ErrorExamples.notAllowed, "Method Not Allowed"),
|
||||||
)
|
errorVariant(StatusCode.Conflict, ErrorExamples.conflict, "Conflict"),
|
||||||
) { case (code, _) => code == StatusCode.BadRequest },
|
errorVariant(StatusCode(418), ErrorExamples.teapot, "I'm a teapot (In Development)"),
|
||||||
|
errorVariant(StatusCode.UnprocessableEntity, ErrorExamples.validation, "Validation Error"),
|
||||||
oneOfVariantValueMatcher(
|
errorVariant(StatusCode.InternalServerError, ErrorExamples.internal, "Internal Server Error"),
|
||||||
statusCode.and(
|
errorVariant(StatusCode.ServiceUnavailable, ErrorExamples.unavailable, "Service Unavailable")
|
||||||
jsonBody[ErrorResponse]
|
|
||||||
.description("Unauthorized")
|
|
||||||
.example(ErrorExamples.unauthorized)
|
|
||||||
)
|
|
||||||
) { case (code, _) => code == StatusCode.Unauthorized },
|
|
||||||
|
|
||||||
oneOfVariantValueMatcher(
|
|
||||||
statusCode.and(
|
|
||||||
jsonBody[ErrorResponse]
|
|
||||||
.description("Forbidden")
|
|
||||||
.example(ErrorExamples.forbidden)
|
|
||||||
)
|
|
||||||
) { case (code, _) => code == StatusCode.Forbidden },
|
|
||||||
|
|
||||||
oneOfVariantValueMatcher(
|
|
||||||
statusCode.and(
|
|
||||||
jsonBody[ErrorResponse]
|
|
||||||
.description("Not Found")
|
|
||||||
.example(ErrorExamples.notFound)
|
|
||||||
)
|
|
||||||
) { case (code, _) => code == StatusCode.NotFound },
|
|
||||||
|
|
||||||
oneOfVariantValueMatcher(
|
|
||||||
statusCode.and(
|
|
||||||
jsonBody[ErrorResponse]
|
|
||||||
.description("Method Not Allowed")
|
|
||||||
.example(ErrorExamples.notAllowed)
|
|
||||||
)
|
|
||||||
) { case (code, _) => code == StatusCode.MethodNotAllowed },
|
|
||||||
|
|
||||||
oneOfVariantValueMatcher(
|
|
||||||
statusCode.and(
|
|
||||||
jsonBody[ErrorResponse]
|
|
||||||
.description("Conflict")
|
|
||||||
.example(ErrorExamples.conflict)
|
|
||||||
)
|
|
||||||
) { case (code, _) => code == StatusCode.Conflict },
|
|
||||||
|
|
||||||
oneOfVariantValueMatcher(
|
|
||||||
statusCode.and(
|
|
||||||
jsonBody[ErrorResponse]
|
|
||||||
.description("I'm a teapot (In Development)")
|
|
||||||
.example(ErrorExamples.teapot)
|
|
||||||
)
|
|
||||||
) { case (code, _) => code == StatusCode(418) },
|
|
||||||
|
|
||||||
oneOfVariantValueMatcher(
|
|
||||||
statusCode.and(
|
|
||||||
jsonBody[ErrorResponse]
|
|
||||||
.description("Validation Error")
|
|
||||||
.example(ErrorExamples.validation)
|
|
||||||
)
|
|
||||||
) { case (code, _) => code == StatusCode.UnprocessableEntity },
|
|
||||||
|
|
||||||
oneOfVariantValueMatcher(
|
|
||||||
statusCode.and(
|
|
||||||
jsonBody[ErrorResponse]
|
|
||||||
.description("Internal Server Error")
|
|
||||||
.example(ErrorExamples.internal)
|
|
||||||
)
|
|
||||||
) { case (code, _) => code == StatusCode.InternalServerError },
|
|
||||||
|
|
||||||
oneOfVariantValueMatcher(
|
|
||||||
statusCode.and(
|
|
||||||
jsonBody[ErrorResponse]
|
|
||||||
.description("Service Unavailable")
|
|
||||||
.example(ErrorExamples.unavailable)
|
|
||||||
)
|
|
||||||
) { case (code, _) => code == StatusCode.ServiceUnavailable }
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.out(jsonBody[BaseResponse])
|
.out(jsonBody[BaseResponse])
|
||||||
|
|||||||
@ -1,19 +1,23 @@
|
|||||||
package org.yobble.chat_private_service.api.route
|
package org.yobble.chat_private_service.api.route
|
||||||
|
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import org.yobble.chat_private_service.api.endpoint.{ErrorsEndpoint, PingEndpoint, TestErrorEndpoint}
|
import org.http4s.Request
|
||||||
import org.yobble.module.response.{BaseResponse, ErrorDetail, ErrorResponse}
|
import org.yobble.module.middleware.RealIpAndUserAgentMiddleware.{ipKey, uaKey}
|
||||||
import org.yobble.module.service.auth.{AuthClient, TokenServiceException}
|
import sttp.tapir.server.http4s.Http4sServerOptions
|
||||||
import org.yobble.module.service.profile.ProfileClient
|
|
||||||
import org.yobble.module.util.errorByStatus
|
|
||||||
import sttp.model.StatusCode
|
import sttp.model.StatusCode
|
||||||
import sttp.tapir.server.ServerEndpoint
|
import sttp.tapir.server.ServerEndpoint
|
||||||
import sttp.tapir.server.ServerEndpoint.Full
|
|
||||||
|
import org.yobble.chat_private_service.api.endpoint.{ErrorsEndpoint, PingEndpoint, TestErrorEndpoint}
|
||||||
|
import org.yobble.module.response.{BaseResponse, ErrorDetail, ErrorResponse}
|
||||||
|
import org.yobble.module.util.{ErrorUtils, errorByStatus}
|
||||||
|
import org.yobble.module.service.auth.{AuthClient, TokenServiceException}
|
||||||
|
import org.yobble.module.service.profile.ProfileClient
|
||||||
|
import org.yobble.module.util.ErrorExamples
|
||||||
|
|
||||||
|
|
||||||
object AllServerEndpoints {
|
object AllServerEndpoints {
|
||||||
def all(authClient: AuthClient, profileClient: ProfileClient): List[ServerEndpoint[Any, IO]] = List(
|
def all(authClient: AuthClient, profileClient: ProfileClient): List[ServerEndpoint[Any, IO]] = List(
|
||||||
PingEndpoint.pingEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse("fine", "pong"))),
|
PingEndpoint.pingEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse("fine", "pong"))),
|
||||||
|
|
||||||
ErrorsEndpoint.errorsEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse("fine", "errors"))),
|
ErrorsEndpoint.errorsEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse("fine", "errors"))),
|
||||||
|
|
||||||
TestErrorEndpoint.testErrorEndpoint
|
TestErrorEndpoint.testErrorEndpoint
|
||||||
@ -21,37 +25,29 @@ object AllServerEndpoints {
|
|||||||
val cleanToken = token.stripPrefix("Bearer ").trim
|
val cleanToken = token.stripPrefix("Bearer ").trim
|
||||||
val ip = "127.0.0.1"
|
val ip = "127.0.0.1"
|
||||||
val ua = "scala-client"
|
val ua = "scala-client"
|
||||||
|
// val ip = req.attributes.lookup(ipKey).getOrElse("unknown")
|
||||||
|
// val ua = req.attributes.lookup(uaKey).getOrElse("unknown")
|
||||||
|
//
|
||||||
|
// println(s"ip: $ip")
|
||||||
|
// println(s"ua: $ua")
|
||||||
|
|
||||||
authClient.getCurrentUser(cleanToken, ip, ua).attempt.map {
|
authClient.getCurrentUser(cleanToken, ip, ua).attempt.map {
|
||||||
case Left(TokenServiceException(code, msg)) =>
|
case Left(TokenServiceException(_, msg)) =>
|
||||||
Left((code, ErrorResponse(
|
Left(ErrorUtils.unauthorized(msg))
|
||||||
status = code.code.toString,
|
|
||||||
errors = List(ErrorDetail(field = "Authorization", message = msg))
|
|
||||||
)))
|
|
||||||
|
|
||||||
// case Left(_) =>
|
|
||||||
// val code = StatusCode.ServiceUnavailable
|
|
||||||
// Left((code, errorByStatus(code))) // <-- Исправлено!
|
|
||||||
|
|
||||||
case Left(_) =>
|
case Left(_) =>
|
||||||
val code = StatusCode.ServiceUnavailable
|
Left(ErrorUtils.serviceUnavailableError("Token service unavailable"))
|
||||||
Left((code, ErrorResponse(
|
|
||||||
status = "error",
|
|
||||||
errors = List(ErrorDetail(field = "request", message = "Token service unavailable"))
|
|
||||||
)))
|
|
||||||
|
|
||||||
case Right(_) =>
|
case Right(_) =>
|
||||||
Right(token)
|
Right(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.serverLogic { _ =>
|
.serverLogic { _ => maybeCode =>
|
||||||
maybeCode =>
|
|
||||||
val status = maybeCode
|
val status = maybeCode
|
||||||
.flatMap(code => StatusCode.safeApply(code).toOption)
|
.flatMap(code => StatusCode.safeApply(code).toOption)
|
||||||
.getOrElse(StatusCode.InternalServerError)
|
.getOrElse(StatusCode.InternalServerError)
|
||||||
|
|
||||||
IO.pure(Left((status, errorByStatus(status)))) // ❗ важно: кортеж!
|
IO.pure(Left(errorByStatus(status)))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import org.yobble.module.service.auth.AuthClient
|
|||||||
import org.yobble.module.service.profile.ProfileClient
|
import org.yobble.module.service.profile.ProfileClient
|
||||||
import sttp.tapir.server.http4s.Http4sServerInterpreter
|
import sttp.tapir.server.http4s.Http4sServerInterpreter
|
||||||
import sttp.tapir.swagger.bundle.SwaggerInterpreter
|
import sttp.tapir.swagger.bundle.SwaggerInterpreter
|
||||||
|
import org.yobble.module.middleware.RealIpAndUserAgentMiddleware
|
||||||
|
|
||||||
class Routes(authClient: AuthClient, profileClient: ProfileClient) {
|
class Routes(authClient: AuthClient, profileClient: ProfileClient) {
|
||||||
private val allServerEndpoints = AllServerEndpoints.all(authClient, profileClient)
|
private val allServerEndpoints = AllServerEndpoints.all(authClient, profileClient)
|
||||||
@ -21,5 +22,6 @@ class Routes(authClient: AuthClient, profileClient: ProfileClient) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// val all: HttpRoutes[IO] = RealIpAndUserAgentMiddleware(docsRoutes <+> httpRoutes)
|
||||||
val all: HttpRoutes[IO] = docsRoutes <+> httpRoutes
|
val all: HttpRoutes[IO] = docsRoutes <+> httpRoutes
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
package org.yobble.module.middleware
|
||||||
|
|
||||||
|
import cats.data.Kleisli
|
||||||
|
import cats.effect.IO
|
||||||
|
import cats.effect.unsafe.implicits.global
|
||||||
|
import org.http4s.{HttpRoutes, Request}
|
||||||
|
import org.typelevel.vault.Key
|
||||||
|
import org.typelevel.ci.CIString
|
||||||
|
|
||||||
|
|
||||||
|
object RealIpAndUserAgentMiddleware {
|
||||||
|
// Эти ключи создаются один раз при запуске приложения
|
||||||
|
lazy val ipKey: Key[String] = Key.newKey[IO, String].unsafeRunSync()
|
||||||
|
lazy val uaKey: Key[String] = Key.newKey[IO, String].unsafeRunSync()
|
||||||
|
|
||||||
|
def apply(routes: HttpRoutes[IO]): HttpRoutes[IO] = Kleisli { req =>
|
||||||
|
val ip = req.headers
|
||||||
|
.get(CIString("X-Real-IP")).map(_.head.value)
|
||||||
|
.orElse(req.headers.get(CIString("X-Forwarded-For")).map(_.head.value.split(",").head.trim))
|
||||||
|
.getOrElse(req.remote.map(_.host.toString).getOrElse("unknown"))
|
||||||
|
|
||||||
|
val ua = req.headers.get(CIString("User-Agent")).map(_.head.value).getOrElse("unknown")
|
||||||
|
|
||||||
|
val enriched = req.withAttributes(
|
||||||
|
req.attributes
|
||||||
|
.insert(ipKey, ip)
|
||||||
|
.insert(uaKey, ua)
|
||||||
|
)
|
||||||
|
|
||||||
|
routes(enriched)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,4 +1,16 @@
|
|||||||
package org.yobble.module.response
|
package org.yobble.module.response
|
||||||
|
|
||||||
final case class ErrorDetail(field: String, message: String)
|
import io.circe.{Encoder, Json}
|
||||||
final case class ErrorResponse(status: String = "error", errors: List[ErrorDetail])
|
import io.circe.generic.semiauto._
|
||||||
|
|
||||||
|
case class ErrorDetail(field: String, message: String)
|
||||||
|
case class ErrorResponse(status: String = "error", code: Int, errors: List[ErrorDetail])
|
||||||
|
|
||||||
|
object ErrorResponse {
|
||||||
|
// Добавляем encoder для вложенного класса
|
||||||
|
implicit val errorDetailEncoder: Encoder[ErrorDetail] = deriveEncoder[ErrorDetail]
|
||||||
|
|
||||||
|
// Encoder без поля code
|
||||||
|
implicit val errorResponseEncoder: Encoder[ErrorResponse] =
|
||||||
|
deriveEncoder[ErrorResponse].mapJsonObject(_.remove("code"))
|
||||||
|
}
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class AuthClient(client: Client[IO]) {
|
|||||||
method = POST,
|
method = POST,
|
||||||
uri = Uri.unsafeFromString(AppConfig.TOKEN_SERVICE + "/decode")
|
uri = Uri.unsafeFromString(AppConfig.TOKEN_SERVICE + "/decode")
|
||||||
).withEntity(requestBody)
|
).withEntity(requestBody)
|
||||||
|
.withHttpVersion(HttpVersion.`HTTP/2`)
|
||||||
|
|
||||||
println(s"$req")
|
println(s"$req")
|
||||||
|
|
||||||
|
|||||||
@ -5,87 +5,86 @@ import sttp.model.StatusCode
|
|||||||
|
|
||||||
object ErrorUtils {
|
object ErrorUtils {
|
||||||
def badRequest(message: String = "Bad request"): ErrorResponse =
|
def badRequest(message: String = "Bad request"): ErrorResponse =
|
||||||
ErrorResponse(errors = List(ErrorDetail("general", message)))
|
ErrorResponse(code = 400, errors = List(ErrorDetail("general", message)))
|
||||||
|
|
||||||
def unauthorized(message: String = "Unauthorized"): ErrorResponse =
|
def unauthorized(message: String = "Unauthorized"): ErrorResponse =
|
||||||
ErrorResponse(errors = List(ErrorDetail("login", message)))
|
ErrorResponse(code = 401, errors = List(ErrorDetail("login", message)))
|
||||||
|
|
||||||
def forbidden(message: String = "Forbidden"): ErrorResponse =
|
def forbidden(message: String = "Forbidden"): ErrorResponse =
|
||||||
ErrorResponse(errors = List(ErrorDetail("permission", message)))
|
ErrorResponse(code = 403, errors = List(ErrorDetail("permission", message)))
|
||||||
|
|
||||||
def notFound(message: String = "Not found"): ErrorResponse =
|
def notFound(message: String = "Not found"): ErrorResponse =
|
||||||
ErrorResponse(errors = List(ErrorDetail("resource", message)))
|
ErrorResponse(code = 404, errors = List(ErrorDetail("resource", message)))
|
||||||
|
|
||||||
def methodNotAllowed(message: String = "Method not allowed"): ErrorResponse =
|
def methodNotAllowed(message: String = "Method not allowed"): ErrorResponse =
|
||||||
ErrorResponse(errors = List(ErrorDetail("method", message)))
|
ErrorResponse(code = 405, errors = List(ErrorDetail("method", message)))
|
||||||
|
|
||||||
def conflict(message: String = "Conflict"): ErrorResponse =
|
def conflict(message: String = "Conflict"): ErrorResponse =
|
||||||
ErrorResponse(errors = List(ErrorDetail("conflict", message)))
|
ErrorResponse(code = 409, errors = List(ErrorDetail("conflict", message)))
|
||||||
|
|
||||||
def imATeapot(message: String = "This feature is under development"): ErrorResponse =
|
def imATeapot(message: String = "This feature is under development"): ErrorResponse =
|
||||||
ErrorResponse(errors = List(ErrorDetail("debug", message)))
|
ErrorResponse(code = 418, errors = List(ErrorDetail("debug", message)))
|
||||||
|
|
||||||
def validation(errors: List[(String, String)]): ErrorResponse =
|
def validation(errors: List[(String, String)]): ErrorResponse =
|
||||||
ErrorResponse(errors = errors.map((ErrorDetail.apply _).tupled))
|
ErrorResponse(code = 422, errors = errors.map((ErrorDetail.apply _).tupled))
|
||||||
|
|
||||||
def internalServerError(message: String = "Internal Server Error"): ErrorResponse =
|
def internalServerError(message: String = "Internal Server Error"): ErrorResponse =
|
||||||
ErrorResponse(errors = List(ErrorDetail("server", message)))
|
ErrorResponse(code = 500, errors = List(ErrorDetail("server", message)))
|
||||||
|
|
||||||
def serviceUnavailableError(message: String = "Service unavailable"): ErrorResponse =
|
def serviceUnavailableError(message: String = "Service unavailable"): ErrorResponse =
|
||||||
ErrorResponse(errors = List(ErrorDetail("request", message)))
|
ErrorResponse(code = 503, errors = List(ErrorDetail("request", message)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object ErrorExamples {
|
object ErrorExamples {
|
||||||
|
|
||||||
val unauthorized: ErrorResponse = ErrorResponse(
|
val badRequest: ErrorResponse =
|
||||||
status = "error",
|
ErrorResponse(
|
||||||
errors = List(ErrorDetail("login", "Invalid login or password"))
|
code = 400,
|
||||||
)
|
|
||||||
|
|
||||||
val badRequest: ErrorResponse = ErrorResponse(
|
|
||||||
status = "error",
|
|
||||||
errors = List(ErrorDetail("general", "Bad request syntax or invalid parameters"))
|
errors = List(ErrorDetail("general", "Bad request syntax or invalid parameters"))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val unauthorized: ErrorResponse = ErrorResponse(
|
||||||
|
code = 401,
|
||||||
|
errors = List(ErrorDetail("login", "Invalid login or password"))
|
||||||
|
)
|
||||||
|
|
||||||
val forbidden: ErrorResponse = ErrorResponse(
|
val forbidden: ErrorResponse = ErrorResponse(
|
||||||
status = "error",
|
code = 403,
|
||||||
errors = List(ErrorDetail("permission", "You don't have access to this resource"))
|
errors = List(ErrorDetail("permission", "You don't have access to this resource"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val notFound: ErrorResponse = ErrorResponse(
|
val notFound: ErrorResponse = ErrorResponse(
|
||||||
status = "error",
|
code = 404,
|
||||||
errors = List(ErrorDetail("resource", "Requested resource not found"))
|
errors = List(ErrorDetail("resource", "Requested resource not found"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val notAllowed: ErrorResponse = ErrorResponse(
|
val notAllowed: ErrorResponse = ErrorResponse(
|
||||||
status = "error",
|
code = 405,
|
||||||
errors = List(ErrorDetail("resource", "Method not allowed on this endpoint"))
|
errors = List(ErrorDetail("resource", "Method not allowed on this endpoint"))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
val conflict: ErrorResponse = ErrorResponse(
|
val conflict: ErrorResponse = ErrorResponse(
|
||||||
status = "error",
|
code = 409,
|
||||||
errors = List(ErrorDetail("conflict", "Resource already exists or conflict occurred"))
|
errors = List(ErrorDetail("conflict", "Resource already exists or conflict occurred"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val teapot: ErrorResponse = ErrorResponse(
|
val teapot: ErrorResponse = ErrorResponse(
|
||||||
status = "error",
|
code = 418,
|
||||||
errors = List(ErrorDetail("debug", "This feature is under development"))
|
errors = List(ErrorDetail("debug", "This feature is under development"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val validation: ErrorResponse = ErrorResponse(
|
val validation: ErrorResponse = ErrorResponse(
|
||||||
status = "error",
|
code = 422,
|
||||||
errors = List(ErrorDetail("login", "Login must not contain whitespace characters"))
|
errors = List(ErrorDetail("login", "Login must not contain whitespace characters"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val internal: ErrorResponse = ErrorResponse(
|
val internal: ErrorResponse = ErrorResponse(
|
||||||
status = "error",
|
code = 500,
|
||||||
errors = List(ErrorDetail("server", "An unexpected error occurred. Please try again later."))
|
errors = List(ErrorDetail("server", "An unexpected error occurred. Please try again later."))
|
||||||
)
|
)
|
||||||
|
|
||||||
val unavailable: ErrorResponse = ErrorResponse(
|
val unavailable: ErrorResponse = ErrorResponse(
|
||||||
status = "error",
|
code = 503,
|
||||||
errors = List(ErrorDetail("request", "Service unavailable."))
|
errors = List(ErrorDetail("request", "Service unavailable."))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user