add test error

This commit is contained in:
cheykrym 2025-08-06 03:54:05 +03:00
parent 2c63c8af89
commit 337c222548
6 changed files with 74 additions and 37 deletions

View File

@ -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] =

View File

@ -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

View File

@ -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))
}

View File

@ -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"))
}

View File

@ -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

View File

@ -5,34 +5,34 @@ 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 {
@ -40,37 +40,37 @@ object ErrorExamples {
val badRequest: ErrorResponse =
ErrorResponse(
code = 400,
errors = List(ErrorDetail("general", "Bad request syntax or invalid parameters"))
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."))
)
}