All Downloads are FREE. Search and download functionalities are using the official Maven repository.

liewhite.rpc.Endpoint.scala Maven / Gradle / Ivy

There is a newer version: 4.2.3
Show newest version
package liewhite.rpc

import zio.*
import com.rabbitmq.client.AMQP
import com.rabbitmq.client.Delivery

import liewhite.json.{*, given}

// 业务异常
case class EndpointResponse[T](code: Int, internalCode: Int = 0, msg: String = "", data: Option[T]) derives Schema
case class EndpointException(code: Int, internalCode: Int = 0, msg: String = "")
    extends Exception(s"code: $code, msg: $msg") derives Schema

class Endpoint[
  IN: Schema,
  OUT: Schema
](val route: String) {
  def listen(
    callback: IN => Task[OUT]
  ): ZIO[RpcServer & Scope, Throwable, Unit] =
    for {
      server <- ZIO.service[RpcServer]
      _ <- server.listen(
             route,
             req => {
               // 要区分fail和cause, fail需要正常返回, cause直接500
               (for {
                 body <- ZIO
                           .fromEither(String(req.getBody()).fromJson[IN])
                           .mapError(err => EndpointException(400, 0, err.toString()))
                 res <- ZIO
                          .attemptBlocking(callback(body).map(r => EndpointResponse(200, 200, "ok", Some(r))))
                          .flatten
                          .catchSome { case e @ EndpointException(code, icode, msg) =>
                            ZIO.succeed(
                              EndpointResponse[OUT](code, icode, msg, None)
                            )
                          }
                          .catchAllCause { err =>
                            ZIO.succeed(
                              EndpointResponse[OUT](500, 500, err.toString(), None)
                            )
                          }
                 ser = res.toJson.asString
               } yield ser).catchSome {
                 case e @ EndpointException(code, internalCode, msg) => {
                   ZIO.succeed(e.toJson.asString)
                 }
               }
             }
           )
      _ <- server.listen(
             route + ".doc",
             _ => {
               val in  = summon[Schema[IN]]
               val out = summon[Schema[OUT]]
               ZIO.succeed(
                 EndpointResponse[String](
                   200,
                   200,
                   "",
                   Some(s"""|IN:
                            |${in.ast}
                            |
                            |OUT:
                            |${out.ast}
                            |""".stripMargin)
                 ).toJson.asString
               )
             }
           )
    } yield ()

  def call(req: IN, timeout: Duration = 30.second): ZIO[RpcClient, Throwable, OUT] =
    (for {
      client <- ZIO.service[RpcClient]
      res <- client
               .call(route, req.toJson.asString, timeout = timeout)
      body <- {
        if (res.code != 0) {
          ZIO.fail(EndpointException(500, 500, res.msg))
        } else {
          ZIO
            .fromEither(res.data.fromJson[EndpointResponse[OUT]])
            .mapError(err => EndpointException(500, 500, "response can't be decode: " + err.toString()))
        }
      }
      out <- {
        if (body.code >= 300) {
          ZIO.fail(EndpointException(body.code, body.internalCode, body.msg))
        } else {
          ZIO.attempt(body.data.get)
        }
      }
    } yield out).catchSome {
      case noroute: NoRouteException => {
        ZIO.fail(EndpointException(404, 404, s"no route for ${noroute.route}"))
      }
    }

  def send(req: IN): ZIO[RpcClient, Throwable, Unit] =
    for {
      client <- ZIO.service[RpcClient]
      _ <- client
             .send(route, req.toJson.toArray)
    } yield ()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy