From dd388ca2c1b6ef9599e8b1b1dcb1665cbd113ab3 Mon Sep 17 00:00:00 2001 From: cheykrym Date: Tue, 8 Jul 2025 18:59:52 +0300 Subject: [PATCH] update --- build.sbt | 7 ++- .../chat_private_service/AppModule.scala | 19 +++++++ .../yobble/chat_private_service/Main.scala | 27 +++++----- .../chat_private_service/api/Routes.scala | 39 --------------- .../api/endpoint/ErrorsEndpoint.scala | 5 +- .../api/endpoint/PingEndpoint.scala | 2 +- .../api/endpoint/TestErrorEndpoint.scala | 17 ++++--- .../api/route/AllServerEndpoints.scala | 49 +++++++++++++++++++ .../api/route/Routes.scala | 25 ++++++++++ .../config/AppConfig.scala | 2 + .../middleware/GlobalErrorHandler.scala | 5 +- .../response/BaseResponse.scala | 2 +- .../response/ErrorResponse.scala | 2 +- .../yobble/module/service/HttpClient.scala | 12 +++++ .../module/service/auth/AuthClient.scala | 44 +++++++++++++++++ .../module/service/auth/AuthModels.scala | 8 +++ .../service/auth/DecodeTokenRequest.scala | 8 +++ .../service/auth/TokenServiceException.scala | 12 +++++ .../service/profile/ProfileClient.scala | 46 +++++++++++++++++ .../profile/ProfilesByUserIdsRequest.scala | 6 +++ .../api => module}/util/ErrorUtils.scala | 5 +- 21 files changed, 270 insertions(+), 72 deletions(-) create mode 100644 src/main/scala/org/yobble/chat_private_service/AppModule.scala delete mode 100644 src/main/scala/org/yobble/chat_private_service/api/Routes.scala create mode 100644 src/main/scala/org/yobble/chat_private_service/api/route/AllServerEndpoints.scala create mode 100644 src/main/scala/org/yobble/chat_private_service/api/route/Routes.scala rename src/main/scala/org/yobble/{chat_private_service => module}/middleware/GlobalErrorHandler.scala (85%) rename src/main/scala/org/yobble/{chat_private_service/api => module}/response/BaseResponse.scala (54%) rename src/main/scala/org/yobble/{chat_private_service/api => module}/response/ErrorResponse.scala (73%) create mode 100644 src/main/scala/org/yobble/module/service/HttpClient.scala create mode 100644 src/main/scala/org/yobble/module/service/auth/AuthClient.scala create mode 100644 src/main/scala/org/yobble/module/service/auth/AuthModels.scala create mode 100644 src/main/scala/org/yobble/module/service/auth/DecodeTokenRequest.scala create mode 100644 src/main/scala/org/yobble/module/service/auth/TokenServiceException.scala create mode 100644 src/main/scala/org/yobble/module/service/profile/ProfileClient.scala create mode 100644 src/main/scala/org/yobble/module/service/profile/ProfilesByUserIdsRequest.scala rename src/main/scala/org/yobble/{chat_private_service/api => module}/util/ErrorUtils.scala (96%) diff --git a/build.sbt b/build.sbt index acd93c4..fc6ebbf 100644 --- a/build.sbt +++ b/build.sbt @@ -17,10 +17,13 @@ lazy val root = (project in file(".")) "org.http4s" %% "http4s-dsl" % http4sVersion, "org.http4s" %% "http4s-circe" % http4sVersion, "io.circe" %% "circe-generic" % circeVersion, + "co.fs2" %% "fs2-io" % "3.10.1", // Tapir + Swagger "com.softwaremill.sttp.tapir" %% "tapir-http4s-server" % tapirVersion, "com.softwaremill.sttp.tapir" %% "tapir-json-circe" % tapirVersion, - "com.softwaremill.sttp.tapir" %% "tapir-swagger-ui-bundle" % tapirVersion - ) + "com.softwaremill.sttp.tapir" %% "tapir-swagger-ui-bundle" % tapirVersion, + "com.softwaremill.sttp.tapir" %% "tapir-openapi-docs" % tapirVersion, + "com.softwaremill.sttp.apispec" %% "openapi-circe-yaml" % "0.11.10" +) ) diff --git a/src/main/scala/org/yobble/chat_private_service/AppModule.scala b/src/main/scala/org/yobble/chat_private_service/AppModule.scala new file mode 100644 index 0000000..247665f --- /dev/null +++ b/src/main/scala/org/yobble/chat_private_service/AppModule.scala @@ -0,0 +1,19 @@ +package org.yobble.chat_private_service + +import cats.effect.* +import org.http4s.client.Client +import org.yobble.chat_private_service.api.route.Routes +import org.yobble.module.service.auth.AuthClient +import org.yobble.module.service.profile.ProfileClient +import org.yobble.module.service.HttpClient + +object AppModule { + + def create: Resource[IO, Routes] = + for { + httpClient <- HttpClient.create + authClient = new AuthClient(httpClient) + profileClient = new ProfileClient(httpClient) + routes = new Routes(authClient, profileClient) + } yield routes +} diff --git a/src/main/scala/org/yobble/chat_private_service/Main.scala b/src/main/scala/org/yobble/chat_private_service/Main.scala index f7f3c87..2399037 100644 --- a/src/main/scala/org/yobble/chat_private_service/Main.scala +++ b/src/main/scala/org/yobble/chat_private_service/Main.scala @@ -12,22 +12,21 @@ import org.http4s.server.Router import cats.implicits.toSemigroupKOps import com.comcast.ip4s.{Port, host} import org.http4s.circe.CirceEntityCodec.* -import org.yobble.chat_private_service.api.Routes +import org.yobble.chat_private_service.api.route.Routes import org.yobble.chat_private_service.config.AppConfig -import org.yobble.chat_private_service.middleware.GlobalErrorHandler +import org.yobble.module.middleware.GlobalErrorHandler object Main extends IOApp { override def run(args: List[String]): IO[ExitCode] = - EmberServerBuilder - .default[IO] - .withHost(host"0.0.0.0") - .withPort(port = Port.fromInt(AppConfig.PORT).get) - .withHttpApp(GlobalErrorHandler.withGlobalErrorHandler(Routes.all.orNotFound)) - .build - .use(_ => - IO.println(s"Server running at http://${AppConfig.HOST}:${AppConfig.PORT}") *> - IO.println(s"Swagger UI available at http://${AppConfig.HOST}:${AppConfig.PORT}/docs") *> - IO.never - ) - .as(ExitCode.Success) + AppModule.create.flatMap { routes => // ✅ создаём routes тут + EmberServerBuilder.default[IO] + .withHost(host"0.0.0.0") + .withPort(Port.fromInt(AppConfig.PORT).get) + .withHttpApp(GlobalErrorHandler.withGlobalErrorHandler(routes.all.orNotFound)) + .build + }.use(_ => // ✅ внутри use + IO.println(s"Server running at http://${AppConfig.HOST}:${AppConfig.PORT}") *> + IO.println(s"Swagger UI available at http://${AppConfig.HOST}:${AppConfig.PORT}/docs") *> + IO.never + ).as(ExitCode.Success) } diff --git a/src/main/scala/org/yobble/chat_private_service/api/Routes.scala b/src/main/scala/org/yobble/chat_private_service/api/Routes.scala deleted file mode 100644 index 9fb2075..0000000 --- a/src/main/scala/org/yobble/chat_private_service/api/Routes.scala +++ /dev/null @@ -1,39 +0,0 @@ -package org.yobble.chat_private_service.api - -import cats.effect.IO -import org.http4s.HttpRoutes -import sttp.tapir.server.http4s.* -import sttp.tapir.swagger.bundle.SwaggerInterpreter -import org.yobble.chat_private_service.api.endpoint.PingEndpoint.* -import org.yobble.chat_private_service.api.endpoint.ErrorsEndpoint.* -import org.yobble.chat_private_service.api.endpoint.TestErrorEndpoint.* -import cats.syntax.semigroupk.* -import org.yobble.chat_private_service.api.response.BaseResponse -import sttp.model.StatusCode -import org.yobble.chat_private_service.api.util.errorByStatus - -object Routes { - private val pingRoute = Http4sServerInterpreter[IO]().toRoutes( - pingEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse("fine", "pong"))) - ) - - private val errorsRoute = Http4sServerInterpreter[IO]().toRoutes( - errorsEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse("fine", "errors"))) - ) - - private val testErrorRoute = Http4sServerInterpreter[IO]().toRoutes( - testErrorEndpoint.serverLogic((code: Int) => - IO.pure(Left(errorByStatus(StatusCode.safeApply(code).getOrElse(StatusCode.InternalServerError)))) - ) - ) - - private val docsRoutes = Http4sServerInterpreter[IO]().toRoutes( - SwaggerInterpreter().fromEndpoints[IO]( - List(pingEndpoint, errorsEndpoint, testErrorEndpoint), - "chat_private_service API", - "1.0" - ) - ) - - val all: HttpRoutes[IO] = pingRoute <+> errorsRoute <+> testErrorRoute <+> docsRoutes -} \ No newline at end of file diff --git a/src/main/scala/org/yobble/chat_private_service/api/endpoint/ErrorsEndpoint.scala b/src/main/scala/org/yobble/chat_private_service/api/endpoint/ErrorsEndpoint.scala index 95f346c..cbe2ae8 100644 --- a/src/main/scala/org/yobble/chat_private_service/api/endpoint/ErrorsEndpoint.scala +++ b/src/main/scala/org/yobble/chat_private_service/api/endpoint/ErrorsEndpoint.scala @@ -1,13 +1,12 @@ package org.yobble.chat_private_service.api.endpoint import io.circe.generic.auto.* -import org.yobble.chat_private_service.api.response.BaseResponse import org.yobble.chat_private_service.config.AppConfig +import org.yobble.module.response.{BaseResponse, ErrorResponse} import sttp.tapir.* import sttp.tapir.generic.auto.* import sttp.tapir.json.circe.* -import org.yobble.chat_private_service.api.response.ErrorResponse -import org.yobble.chat_private_service.api.util.ErrorExamples +import org.yobble.module.util.ErrorExamples import sttp.model.StatusCode diff --git a/src/main/scala/org/yobble/chat_private_service/api/endpoint/PingEndpoint.scala b/src/main/scala/org/yobble/chat_private_service/api/endpoint/PingEndpoint.scala index c9117ea..e1259f5 100644 --- a/src/main/scala/org/yobble/chat_private_service/api/endpoint/PingEndpoint.scala +++ b/src/main/scala/org/yobble/chat_private_service/api/endpoint/PingEndpoint.scala @@ -4,8 +4,8 @@ import sttp.tapir._ import sttp.tapir.generic.auto._ import sttp.tapir.json.circe._ import io.circe.generic.auto._ -import org.yobble.chat_private_service.api.response.BaseResponse import org.yobble.chat_private_service.config.AppConfig +import org.yobble.module.response.BaseResponse object PingEndpoint { diff --git a/src/main/scala/org/yobble/chat_private_service/api/endpoint/TestErrorEndpoint.scala b/src/main/scala/org/yobble/chat_private_service/api/endpoint/TestErrorEndpoint.scala index df1d684..8406bec 100644 --- a/src/main/scala/org/yobble/chat_private_service/api/endpoint/TestErrorEndpoint.scala +++ b/src/main/scala/org/yobble/chat_private_service/api/endpoint/TestErrorEndpoint.scala @@ -1,21 +1,26 @@ package org.yobble.chat_private_service.api.endpoint import io.circe.generic.auto.* -import org.yobble.chat_private_service.api.response.{BaseResponse, ErrorResponse} -import org.yobble.chat_private_service.api.util.ErrorExamples -import org.yobble.chat_private_service.config.AppConfig import sttp.model.StatusCode import sttp.tapir.* import sttp.tapir.generic.auto.* import sttp.tapir.json.circe.* - +import sttp.model.headers.WWWAuthenticateChallenge +import org.yobble.module.response.{BaseResponse, ErrorResponse} +import org.yobble.module.util.ErrorExamples object TestErrorEndpoint { - val testErrorEndpoint: Endpoint[Unit, Int, ErrorResponse, BaseResponse, Any] = + val testErrorEndpoint: Endpoint[String, Option[Int], ErrorResponse, BaseResponse, Any] = endpoint.get .in("test-error") - .in(query[Int]("code").description("HTTP status code to simulate (e.g., 401, 404)")) + .name("Test Error") + .securityIn(auth.bearer[String](WWWAuthenticateChallenge.bearer)) + .in( + query[Option[Int]]("code") + .description("Optional HTTP status code to simulate. Defaults to 500.") + .example(Some(404)) + ) .errorOut( oneOf[ErrorResponse]( oneOfVariant(StatusCode.BadRequest, jsonBody[ErrorResponse].description("Bad Request").example(ErrorExamples.badRequest)), diff --git a/src/main/scala/org/yobble/chat_private_service/api/route/AllServerEndpoints.scala b/src/main/scala/org/yobble/chat_private_service/api/route/AllServerEndpoints.scala new file mode 100644 index 0000000..56bac32 --- /dev/null +++ b/src/main/scala/org/yobble/chat_private_service/api/route/AllServerEndpoints.scala @@ -0,0 +1,49 @@ +package org.yobble.chat_private_service.api.route + +import cats.effect.IO +import org.yobble.chat_private_service.api.endpoint.{ErrorsEndpoint, PingEndpoint, TestErrorEndpoint} +import org.yobble.module.response.{BaseResponse, ErrorDetail, ErrorResponse} +import org.yobble.module.service.auth.{AuthClient, TokenServiceException} +import org.yobble.module.service.profile.ProfileClient +import org.yobble.module.util.errorByStatus +import sttp.model.StatusCode +import sttp.tapir.server.ServerEndpoint + +object AllServerEndpoints { + def all(authClient: AuthClient, profileClient: ProfileClient): List[ServerEndpoint[Any, IO]] = List( + PingEndpoint.pingEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse("fine", "pong"))), + + ErrorsEndpoint.errorsEndpoint.serverLogicSuccess(_ => IO.pure(BaseResponse("fine", "errors"))), + + TestErrorEndpoint.testErrorEndpoint + .serverSecurityLogic { token => + val cleanToken = token.stripPrefix("Bearer ").trim + val ip = "127.0.0.1" + val ua = "scala-client" + + authClient.getCurrentUser(cleanToken, ip, ua).attempt.map { + case Left(TokenServiceException(code, msg)) => + Left(ErrorResponse( + status = code.code.toString, + errors = List(ErrorDetail(field = "Authorization", message = msg)) + )) + + case Left(other) => + Left(ErrorResponse( + status = StatusCode.InternalServerError.code.toString, + errors = List(ErrorDetail(field = "Authorization", message = "token_service: service unavailable")) + )) + + case Right(_) => + Right(token) + } + } + .serverLogic { _ => maybeCode => + val status = maybeCode + .flatMap(code => StatusCode.safeApply(code).toOption) + .getOrElse(StatusCode.InternalServerError) + + IO.pure(Left(errorByStatus(status))) + } + ) +} diff --git a/src/main/scala/org/yobble/chat_private_service/api/route/Routes.scala b/src/main/scala/org/yobble/chat_private_service/api/route/Routes.scala new file mode 100644 index 0000000..d5ce28e --- /dev/null +++ b/src/main/scala/org/yobble/chat_private_service/api/route/Routes.scala @@ -0,0 +1,25 @@ +package org.yobble.chat_private_service.api.route + +import cats.effect.IO +import cats.syntax.semigroupk.* +import org.http4s.HttpRoutes +import org.yobble.module.service.auth.AuthClient +import org.yobble.module.service.profile.ProfileClient +import sttp.tapir.server.http4s.Http4sServerInterpreter +import sttp.tapir.swagger.bundle.SwaggerInterpreter + +class Routes(authClient: AuthClient, profileClient: ProfileClient) { + private val allServerEndpoints = AllServerEndpoints.all(authClient, profileClient) + + private val httpRoutes = Http4sServerInterpreter[IO]().toRoutes(allServerEndpoints) + + private val docsRoutes = Http4sServerInterpreter[IO]().toRoutes( + SwaggerInterpreter().fromServerEndpoints[IO]( + allServerEndpoints, + "chat_private_service API", + "1.0" + ) + ) + + val all: HttpRoutes[IO] = docsRoutes <+> httpRoutes +} diff --git a/src/main/scala/org/yobble/chat_private_service/config/AppConfig.scala b/src/main/scala/org/yobble/chat_private_service/config/AppConfig.scala index 6f84138..d43da0f 100644 --- a/src/main/scala/org/yobble/chat_private_service/config/AppConfig.scala +++ b/src/main/scala/org/yobble/chat_private_service/config/AppConfig.scala @@ -5,4 +5,6 @@ object AppConfig { val HOST = "0.0.0.0" val PORT = 5205 + val TOKEN_SERVICE = "https://localhost:5200" + val PROFILE_SERVICE = "" } diff --git a/src/main/scala/org/yobble/chat_private_service/middleware/GlobalErrorHandler.scala b/src/main/scala/org/yobble/module/middleware/GlobalErrorHandler.scala similarity index 85% rename from src/main/scala/org/yobble/chat_private_service/middleware/GlobalErrorHandler.scala rename to src/main/scala/org/yobble/module/middleware/GlobalErrorHandler.scala index e331492..352b998 100644 --- a/src/main/scala/org/yobble/chat_private_service/middleware/GlobalErrorHandler.scala +++ b/src/main/scala/org/yobble/module/middleware/GlobalErrorHandler.scala @@ -1,10 +1,9 @@ -package org.yobble.chat_private_service.middleware - +package org.yobble.module.middleware import cats.effect.Sync import org.http4s.HttpApp import org.http4s.server.middleware.ErrorAction -import org.yobble.chat_private_service.api.util.ErrorUtils +import org.yobble.module.util.ErrorUtils object GlobalErrorHandler { def withGlobalErrorHandler[F[_] : Sync](httpApp: HttpApp[F]): HttpApp[F] = { diff --git a/src/main/scala/org/yobble/chat_private_service/api/response/BaseResponse.scala b/src/main/scala/org/yobble/module/response/BaseResponse.scala similarity index 54% rename from src/main/scala/org/yobble/chat_private_service/api/response/BaseResponse.scala rename to src/main/scala/org/yobble/module/response/BaseResponse.scala index 4ed21b4..dd9b9e2 100644 --- a/src/main/scala/org/yobble/chat_private_service/api/response/BaseResponse.scala +++ b/src/main/scala/org/yobble/module/response/BaseResponse.scala @@ -1,3 +1,3 @@ -package org.yobble.chat_private_service.api.response +package org.yobble.module.response final case class BaseResponse(status: String, message: String) diff --git a/src/main/scala/org/yobble/chat_private_service/api/response/ErrorResponse.scala b/src/main/scala/org/yobble/module/response/ErrorResponse.scala similarity index 73% rename from src/main/scala/org/yobble/chat_private_service/api/response/ErrorResponse.scala rename to src/main/scala/org/yobble/module/response/ErrorResponse.scala index 9b9a2a2..88aea57 100644 --- a/src/main/scala/org/yobble/chat_private_service/api/response/ErrorResponse.scala +++ b/src/main/scala/org/yobble/module/response/ErrorResponse.scala @@ -1,4 +1,4 @@ -package org.yobble.chat_private_service.api.response +package org.yobble.module.response final case class ErrorDetail(field: String, message: String) final case class ErrorResponse(status: String = "error", errors: List[ErrorDetail]) diff --git a/src/main/scala/org/yobble/module/service/HttpClient.scala b/src/main/scala/org/yobble/module/service/HttpClient.scala new file mode 100644 index 0000000..db1956b --- /dev/null +++ b/src/main/scala/org/yobble/module/service/HttpClient.scala @@ -0,0 +1,12 @@ +package org.yobble.module.service + +import cats.effect.* +import org.http4s.client.Client +import org.http4s.ember.client.EmberClientBuilder + +object HttpClient { + def create: Resource[IO, Client[IO]] = + EmberClientBuilder + .default[IO] + .build +} diff --git a/src/main/scala/org/yobble/module/service/auth/AuthClient.scala b/src/main/scala/org/yobble/module/service/auth/AuthClient.scala new file mode 100644 index 0000000..59c46c5 --- /dev/null +++ b/src/main/scala/org/yobble/module/service/auth/AuthClient.scala @@ -0,0 +1,44 @@ +package org.yobble.module.service.auth + +import cats.effect.IO +import org.http4s.Method._ +import org.http4s.client.dsl.io._ +import org.http4s.client.Client +import org.http4s._ +import org.http4s.circe.CirceEntityEncoder._ // for .withEntity(json) +import org.http4s.circe.CirceEntityDecoder._ // for .as[CurrentUser] +import io.circe.syntax._ // for .asJson +import io.circe.generic.auto._ +import org.yobble.chat_private_service.config.AppConfig +import sttp.model.StatusCode + +class AuthClient(client: Client[IO]) { + + def getCurrentUser(token: String, ip: String, userAgent: String): IO[CurrentUser] = { + val requestBody = DecodeTokenRequest( + token = token, + ip = ip, + user_agent = userAgent, + require_permissions = false + ).asJson + + val req = Request[IO]( + method = POST, + uri = Uri.unsafeFromString(AppConfig.TOKEN_SERVICE + "/decode") + ).withEntity(requestBody) + + println(s"$req") + + client.run(req).use { resp => + println(s"$resp") + if (resp.status.isSuccess) { + resp.as[CurrentUser] + } else { + resp.bodyText.compile.string.flatMap { body => + val status = StatusCode.safeApply(resp.status.code).getOrElse(StatusCode.InternalServerError) + IO.raiseError(TokenServiceException(status, s"token_service: $body")) + } + } + } + } +} diff --git a/src/main/scala/org/yobble/module/service/auth/AuthModels.scala b/src/main/scala/org/yobble/module/service/auth/AuthModels.scala new file mode 100644 index 0000000..8b45110 --- /dev/null +++ b/src/main/scala/org/yobble/module/service/auth/AuthModels.scala @@ -0,0 +1,8 @@ +package org.yobble.module.service.auth + +case class CurrentUser( + token: String, + user_id: String, + session_id: String, + permissions: List[String] + ) \ No newline at end of file diff --git a/src/main/scala/org/yobble/module/service/auth/DecodeTokenRequest.scala b/src/main/scala/org/yobble/module/service/auth/DecodeTokenRequest.scala new file mode 100644 index 0000000..1b8cc0e --- /dev/null +++ b/src/main/scala/org/yobble/module/service/auth/DecodeTokenRequest.scala @@ -0,0 +1,8 @@ +package org.yobble.module.service.auth + +case class DecodeTokenRequest( + token: String, + ip: String, + user_agent: String, + require_permissions: Boolean = false + ) diff --git a/src/main/scala/org/yobble/module/service/auth/TokenServiceException.scala b/src/main/scala/org/yobble/module/service/auth/TokenServiceException.scala new file mode 100644 index 0000000..6a1fddd --- /dev/null +++ b/src/main/scala/org/yobble/module/service/auth/TokenServiceException.scala @@ -0,0 +1,12 @@ +package org.yobble.module.service.auth + +import sttp.model.StatusCode + +case class TokenServiceException(code: StatusCode, msg: String) extends Exception(msg) + +object TokenServiceException { + def unapply(e: Throwable): Option[(StatusCode, String)] = e match { + case ex: TokenServiceException => Some((ex.code, ex.msg)) + case _ => None + } +} diff --git a/src/main/scala/org/yobble/module/service/profile/ProfileClient.scala b/src/main/scala/org/yobble/module/service/profile/ProfileClient.scala new file mode 100644 index 0000000..cfa9c76 --- /dev/null +++ b/src/main/scala/org/yobble/module/service/profile/ProfileClient.scala @@ -0,0 +1,46 @@ +package org.yobble.module.service.profile + +import cats.effect.IO +import io.circe.syntax.* +import io.circe.generic.auto.* +import io.circe.Json +import org.http4s.Method.* +import org.http4s.client.dsl.io.* +import org.http4s.{Header, Request, Status, Uri} +import org.http4s.client.Client +import org.typelevel.ci.CIStringSyntax +import org.http4s.circe.* +import java.util.UUID +import org.yobble.chat_private_service.config.AppConfig + +case class ProfileClient(client: Client[IO]) { + + def getProfileByUserId(userId: UUID, token: String): IO[Json] = { + val uri = Uri.unsafeFromString(s"https://$AppConfig.PROFILE_SERVICE/user_id/$userId") + val req = Request[IO](method = GET, uri = uri) + .withHeaders(Header.Raw(ci"Authorization", s"Bearer $token")) + + client.fetch(req) { + case Status.Successful(resp) => resp.as[Json] + case resp => resp.bodyText.compile.string.flatMap(body => + IO.raiseError(new Exception(s"profile_service: ${resp.status.code} $body")) + ) + } + } + + def getProfilesByUserIds(userIds: List[UUID], token: String, userId: UUID): IO[Json] = { + val uri = Uri.unsafeFromString(s"https://$AppConfig.PROFILE_SERVICE/user_ids/internal") + val json = ProfilesByUserIdsRequest(userIds.map(_.toString), userId.toString).asJson + + val req = Request[IO](method = POST, uri = uri) + .withHeaders(Header.Raw(ci"Authorization", s"Bearer $token")) + .withEntity(json) + + client.fetch(req) { + case Status.Successful(resp) => resp.as[Json] + case resp => resp.bodyText.compile.string.flatMap(body => + IO.raiseError(new Exception(s"profile_service: ${resp.status.code} $body")) + ) + } + } +} diff --git a/src/main/scala/org/yobble/module/service/profile/ProfilesByUserIdsRequest.scala b/src/main/scala/org/yobble/module/service/profile/ProfilesByUserIdsRequest.scala new file mode 100644 index 0000000..a6d771d --- /dev/null +++ b/src/main/scala/org/yobble/module/service/profile/ProfilesByUserIdsRequest.scala @@ -0,0 +1,6 @@ +package org.yobble.module.service.profile + +case class ProfilesByUserIdsRequest( + user_ids: List[String], + user_id: String + ) diff --git a/src/main/scala/org/yobble/chat_private_service/api/util/ErrorUtils.scala b/src/main/scala/org/yobble/module/util/ErrorUtils.scala similarity index 96% rename from src/main/scala/org/yobble/chat_private_service/api/util/ErrorUtils.scala rename to src/main/scala/org/yobble/module/util/ErrorUtils.scala index afe9961..dc72a07 100644 --- a/src/main/scala/org/yobble/chat_private_service/api/util/ErrorUtils.scala +++ b/src/main/scala/org/yobble/module/util/ErrorUtils.scala @@ -1,7 +1,7 @@ -package org.yobble.chat_private_service.api.util +package org.yobble.module.util +import org.yobble.module.response.{ErrorDetail, ErrorResponse} import sttp.model.StatusCode -import org.yobble.chat_private_service.api.response.{ErrorDetail, ErrorResponse} object ErrorUtils { def badRequest(message: String = "Bad request"): ErrorResponse = @@ -33,6 +33,7 @@ object ErrorUtils { } object ErrorExamples { + val unauthorized: ErrorResponse = ErrorResponse( status = "error", errors = List(ErrorDetail("login", "Invalid login or password"))