From 337c22254864295ab210f8c1c2414ecc567efea4 Mon Sep 17 00:00:00 2001 From: cheykrym Date: Wed, 6 Aug 2025 03:54:05 +0300 Subject: [PATCH] add test error --- .../endpoint/{ => info}/ErrorsEndpoint.scala | 10 ++--- .../endpoint/{ => info}/PingEndpoint.scala | 10 ++--- .../api/endpoint/info/TestErrorEndpoint.scala | 33 +++++++++++++++ .../api/response/ErrorResponse.scala | 4 +- .../api/route/AllServerEndpoints.scala | 14 ++++--- .../scala_monolith/api/util/ErrorUtils.scala | 40 +++++++++---------- 6 files changed, 74 insertions(+), 37 deletions(-) rename src/main/scala/org/yobble/scala_monolith/api/endpoint/{ => info}/ErrorsEndpoint.scala (92%) rename src/main/scala/org/yobble/scala_monolith/api/endpoint/{ => info}/PingEndpoint.scala (93%) create mode 100644 src/main/scala/org/yobble/scala_monolith/api/endpoint/info/TestErrorEndpoint.scala diff --git a/src/main/scala/org/yobble/scala_monolith/api/endpoint/ErrorsEndpoint.scala b/src/main/scala/org/yobble/scala_monolith/api/endpoint/info/ErrorsEndpoint.scala similarity index 92% rename from src/main/scala/org/yobble/scala_monolith/api/endpoint/ErrorsEndpoint.scala rename to src/main/scala/org/yobble/scala_monolith/api/endpoint/info/ErrorsEndpoint.scala index a54f0be..b0d82dc 100644 --- a/src/main/scala/org/yobble/scala_monolith/api/endpoint/ErrorsEndpoint.scala +++ b/src/main/scala/org/yobble/scala_monolith/api/endpoint/info/ErrorsEndpoint.scala @@ -1,12 +1,12 @@ -package org.yobble.scala_monolith.api.endpoint +package org.yobble.scala_monolith.api.endpoint.info -import io.circe.generic.auto._ +import io.circe.generic.auto.* import org.yobble.scala_monolith.api.response.{BaseResponse, ErrorResponse} -import sttp.tapir._ -import sttp.tapir.generic.auto._ -import sttp.tapir.json.circe._ import org.yobble.scala_monolith.api.util.ErrorExamples import sttp.model.StatusCode +import sttp.tapir.* +import sttp.tapir.generic.auto.* +import sttp.tapir.json.circe.* object ErrorsEndpoint { val errorsEndpoint: PublicEndpoint[Unit, ErrorResponse, BaseResponse, Any] = diff --git a/src/main/scala/org/yobble/scala_monolith/api/endpoint/PingEndpoint.scala b/src/main/scala/org/yobble/scala_monolith/api/endpoint/info/PingEndpoint.scala similarity index 93% rename from src/main/scala/org/yobble/scala_monolith/api/endpoint/PingEndpoint.scala rename to src/main/scala/org/yobble/scala_monolith/api/endpoint/info/PingEndpoint.scala index e27e1f8..9956930 100644 --- a/src/main/scala/org/yobble/scala_monolith/api/endpoint/PingEndpoint.scala +++ b/src/main/scala/org/yobble/scala_monolith/api/endpoint/info/PingEndpoint.scala @@ -1,12 +1,12 @@ -package org.yobble.scala_monolith.api.endpoint +package org.yobble.scala_monolith.api.endpoint.info -import sttp.tapir.* -import sttp.tapir.json.circe.jsonBody -import sttp.tapir.generic.auto.* -import org.yobble.scala_monolith.api.response.{BaseResponse, ErrorResponse} import io.circe.generic.auto.* +import org.yobble.scala_monolith.api.response.{BaseResponse, ErrorResponse} import org.yobble.scala_monolith.api.util.ErrorExamples import sttp.model.StatusCode +import sttp.tapir.* +import sttp.tapir.generic.auto.* +import sttp.tapir.json.circe.jsonBody object PingEndpoint { val pingEndpoint = endpoint diff --git a/src/main/scala/org/yobble/scala_monolith/api/endpoint/info/TestErrorEndpoint.scala b/src/main/scala/org/yobble/scala_monolith/api/endpoint/info/TestErrorEndpoint.scala new file mode 100644 index 0000000..8c29574 --- /dev/null +++ b/src/main/scala/org/yobble/scala_monolith/api/endpoint/info/TestErrorEndpoint.scala @@ -0,0 +1,33 @@ +package org.yobble.scala_monolith.api.endpoint.info + +import io.circe.generic.auto.* +import org.yobble.scala_monolith.api.response.{BaseResponse, ErrorResponse} +import org.yobble.scala_monolith.api.util.ErrorExamples +import sttp.model.StatusCode +import sttp.tapir.* +import sttp.tapir.generic.auto.* +import sttp.tapir.json.circe.* + +object TestErrorEndpoint { + val testErrorEndpoint: PublicEndpoint[Int, ErrorResponse, Unit, Any] = + endpoint.get + .in("test-error" / path[Int]("code").description("The HTTP status code to return")) + .tags(List("Info")) + .name("Test-Error") + .summary("Returns a response with the specified error code") + .errorOut( + oneOf[ErrorResponse]( + oneOfVariant(StatusCode.BadRequest, jsonBody[ErrorResponse].description("Bad Request").example(ErrorExamples.badRequest)), + oneOfVariant(StatusCode.Unauthorized, jsonBody[ErrorResponse].description("Unauthorized").example(ErrorExamples.unauthorized)), + oneOfVariant(StatusCode.Forbidden, jsonBody[ErrorResponse].description("Forbidden").example(ErrorExamples.forbidden)), + oneOfVariant(StatusCode.NotFound, jsonBody[ErrorResponse].description("Not Found").example(ErrorExamples.notFound)), + oneOfVariant(StatusCode.MethodNotAllowed, jsonBody[ErrorResponse].description("Method Not Allowed").example(ErrorExamples.notAllowed)), + oneOfVariant(StatusCode.Conflict, jsonBody[ErrorResponse].description("Conflict").example(ErrorExamples.conflict)), + oneOfVariant(StatusCode(418), jsonBody[ErrorResponse].description("I'm a teapot (In Development)").example(ErrorExamples.teapot)), + oneOfVariant(StatusCode.UnprocessableEntity, jsonBody[ErrorResponse].description("Validation Error").example(ErrorExamples.validation)), + oneOfVariant(StatusCode.InternalServerError, jsonBody[ErrorResponse].description("Internal Server Error").example(ErrorExamples.internal)), + oneOfVariant(StatusCode.ServiceUnavailable, jsonBody[ErrorResponse].description("Service Unavailable").example(ErrorExamples.unavailable)) + ) + ) + .out(statusCode(StatusCode.Ok)) +} diff --git a/src/main/scala/org/yobble/scala_monolith/api/response/ErrorResponse.scala b/src/main/scala/org/yobble/scala_monolith/api/response/ErrorResponse.scala index f687717..abf2b17 100644 --- a/src/main/scala/org/yobble/scala_monolith/api/response/ErrorResponse.scala +++ b/src/main/scala/org/yobble/scala_monolith/api/response/ErrorResponse.scala @@ -11,6 +11,6 @@ object ErrorResponse { implicit val errorDetailEncoder: Encoder[ErrorDetail] = deriveEncoder[ErrorDetail] // Encoder без поля code - implicit val errorResponseEncoder: Encoder[ErrorResponse] = - deriveEncoder[ErrorResponse].mapJsonObject(_.remove("code")) +// implicit val errorResponseEncoder: Encoder[ErrorResponse] = +// deriveEncoder[ErrorResponse].mapJsonObject(_.remove("code")) } diff --git a/src/main/scala/org/yobble/scala_monolith/api/route/AllServerEndpoints.scala b/src/main/scala/org/yobble/scala_monolith/api/route/AllServerEndpoints.scala index c3395a2..f3a14d0 100644 --- a/src/main/scala/org/yobble/scala_monolith/api/route/AllServerEndpoints.scala +++ b/src/main/scala/org/yobble/scala_monolith/api/route/AllServerEndpoints.scala @@ -4,11 +4,12 @@ import cats.effect.IO import doobie.Transactor import org.yobble.scala_monolith.api.endpoint.auth.AuthEndpoints import sttp.tapir.server.ServerEndpoint -import org.yobble.scala_monolith.api.endpoint.PingEndpoint -import org.yobble.scala_monolith.api.endpoint.ErrorsEndpoint -import org.yobble.scala_monolith.api.response.BaseResponse +import org.yobble.scala_monolith.api.endpoint.info.{ErrorsEndpoint, PingEndpoint, TestErrorEndpoint} +import org.yobble.scala_monolith.api.response.{BaseResponse, ErrorResponse} import org.yobble.scala_monolith.repository.UserRepositoryImpl import org.yobble.scala_monolith.service.AuthService +import sttp.model.StatusCode +import org.yobble.scala_monolith.api.util.errorByStatus object AllServerEndpoints { def all(transactor: Transactor[IO]): List[ServerEndpoint[Any, IO]] = { @@ -19,9 +20,12 @@ object AllServerEndpoints { AuthEndpoints.loginEndpoint.serverLogic(authService.login) ) - val otherEndpoints = List( + val otherEndpoints: List[ServerEndpoint[Any, IO]] = List( PingEndpoint.pingEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse(message = "pong"))), - ErrorsEndpoint.errorsEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse(message = "errors"))) + ErrorsEndpoint.errorsEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse(message = "errors"))), + TestErrorEndpoint.testErrorEndpoint.serverLogic(code => { + IO.pure(Left(errorByStatus(StatusCode(code)))) + }) ) authEndpoints ++ otherEndpoints diff --git a/src/main/scala/org/yobble/scala_monolith/api/util/ErrorUtils.scala b/src/main/scala/org/yobble/scala_monolith/api/util/ErrorUtils.scala index d0d8443..277f2b0 100644 --- a/src/main/scala/org/yobble/scala_monolith/api/util/ErrorUtils.scala +++ b/src/main/scala/org/yobble/scala_monolith/api/util/ErrorUtils.scala @@ -5,72 +5,72 @@ import sttp.model.StatusCode object ErrorUtils { def badRequest(message: String = "Bad request"): ErrorResponse = - ErrorResponse(code = 400, errors = List(ErrorDetail("general", message))) + ErrorResponse(code = 400, errors = List(ErrorDetail("Bad request", message))) def unauthorized(message: String = "Unauthorized"): ErrorResponse = - ErrorResponse(code = 401, errors = List(ErrorDetail("login", message))) + ErrorResponse(code = 401, errors = List(ErrorDetail("Unauthorized", message))) def forbidden(message: String = "Forbidden"): ErrorResponse = - ErrorResponse(code = 403, errors = List(ErrorDetail("permission", message))) + ErrorResponse(code = 403, errors = List(ErrorDetail("Forbidden", message))) def notFound(message: String = "Not found"): ErrorResponse = - ErrorResponse(code = 404, errors = List(ErrorDetail("resource", message))) + ErrorResponse(code = 404, errors = List(ErrorDetail("Not found", message))) def methodNotAllowed(message: String = "Method not allowed"): ErrorResponse = - ErrorResponse(code = 405, errors = List(ErrorDetail("request", message))) + ErrorResponse(code = 405, errors = List(ErrorDetail("Method not allowed", message))) def conflict(message: String = "Conflict"): ErrorResponse = - ErrorResponse(code = 409, errors = List(ErrorDetail("conflict", message))) + ErrorResponse(code = 409, errors = List(ErrorDetail("Conflict", message))) def imATeapot(message: String = "This feature is under development"): ErrorResponse = - ErrorResponse(code = 418, errors = List(ErrorDetail("debug", message))) + ErrorResponse(code = 418, errors = List(ErrorDetail("This feature is under development", message))) def validation(errors: List[(String, String)]): ErrorResponse = ErrorResponse(code = 422, errors = errors.map((ErrorDetail.apply _).tupled)) def internalServerError(message: String = "Internal Server Error"): ErrorResponse = - ErrorResponse(code = 500, errors = List(ErrorDetail("server", message))) + ErrorResponse(code = 500, errors = List(ErrorDetail("Internal Server Error", message))) def serviceUnavailableError(message: String = "Service unavailable"): ErrorResponse = - ErrorResponse(code = 503, errors = List(ErrorDetail("proxy", message))) + ErrorResponse(code = 503, errors = List(ErrorDetail("Service unavailable", message))) } object ErrorExamples { val badRequest: ErrorResponse = ErrorResponse( - code = 400, - errors = List(ErrorDetail("general", "Bad request syntax or invalid parameters")) - ) + code = 400, + errors = List(ErrorDetail("Bad request", "Bad request syntax or invalid parameters")) + ) val unauthorized: ErrorResponse = ErrorResponse( code = 401, - errors = List(ErrorDetail("login", "Invalid login or password")) + errors = List(ErrorDetail("Unauthorized", "Invalid login or password")) ) val forbidden: ErrorResponse = ErrorResponse( code = 403, - errors = List(ErrorDetail("permission", "You don't have access to this resource")) + errors = List(ErrorDetail("Forbidden", "You don't have access to this resource")) ) val notFound: ErrorResponse = ErrorResponse( code = 404, - errors = List(ErrorDetail("resource", "Requested resource not found")) + errors = List(ErrorDetail("Not found", "Requested resource not found")) ) val notAllowed: ErrorResponse = ErrorResponse( code = 405, - errors = List(ErrorDetail("request", "Method not allowed on this endpoint")) + errors = List(ErrorDetail("Method not allowed", "Method not allowed on this endpoint")) ) val conflict: ErrorResponse = ErrorResponse( 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( code = 418, - errors = List(ErrorDetail("debug", "This feature is under development")) + errors = List(ErrorDetail("This feature is under development", "This feature is under development")) ) val validation: ErrorResponse = ErrorResponse( @@ -80,12 +80,12 @@ object ErrorExamples { val internal: ErrorResponse = ErrorResponse( code = 500, - errors = List(ErrorDetail("server", "An unexpected error occurred. Please try again later.")) + errors = List(ErrorDetail("Internal Server Error", "An unexpected error occurred. Please try again later.")) ) val unavailable: ErrorResponse = ErrorResponse( code = 503, - errors = List(ErrorDetail("proxy", "Service unavailable.")) + errors = List(ErrorDetail("Service unavailable", "Service unavailable.")) ) }