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

izumi.idealingua.runtime.rpc.IRTServerMethod.scala Maven / Gradle / Ivy

There is a newer version: 1.3.19
Show newest version
package izumi.idealingua.runtime.rpc

import io.circe.Json
import izumi.functional.bio.{Error2, Exit, F, IO2}

trait IRTServerMethod[F[+_, +_], C] {
  self =>
  def methodId: IRTMethodId
  def invoke(context: C, parsedBody: Json): F[Throwable, Json]

  /** Contramap eval on context C2 -> C. If context is missing IRTUnathorizedRequestContextException will raise. */
  final def contramap[C2](updateContext: (C2, Json, IRTMethodId) => F[Throwable, Option[C]])(implicit E: Error2[F]): IRTServerMethod[F, C2] = new IRTServerMethod[F, C2] {
    override def methodId: IRTMethodId = self.methodId
    override def invoke(context: C2, parsedBody: Json): F[Throwable, Json] = {
      updateContext(context, parsedBody, methodId)
        .fromOption(new IRTUnathorizedRequestContextException(s"Unauthorized $methodId call. Context: $context."))
        .flatMap(self.invoke(_, parsedBody))
    }
  }

  /** Wrap invocation with function '(Context, Body)(Method.Invoke) => Result' . */
  final def wrap(middleware: (C, Json) => F[Throwable, Json] => F[Throwable, Json]): IRTServerMethod[F, C] = new IRTServerMethod[F, C] {
    override def methodId: IRTMethodId = self.methodId
    override def invoke(context: C, parsedBody: Json): F[Throwable, Json] = {
      middleware(context, parsedBody)(self.invoke(context, parsedBody))
    }
  }
}

object IRTServerMethod {
  def apply[F[+_, +_]: IO2, C](
    method: IRTMethodWrapper[F, C],
    middleware: IRTOutputMiddleware[F, C],
  ): IRTServerMethod[F, C] = FromWrapper.apply(method, middleware)

  final case class FromWrapper[F[+_, +_]: IO2, C](
    method: IRTMethodWrapper[F, C],
    middleware: IRTOutputMiddleware[F, C],
  ) extends IRTServerMethod[F, C] {
    override def methodId: IRTMethodId = method.signature.id
    @inline override def invoke(context: C, parsedBody: Json): F[Throwable, Json] = {
      val methodId = method.signature.id
      for {
        requestBody <- F.syncThrowable(method.marshaller.decodeRequest[F].apply(IRTJsonBody(methodId, parsedBody))).flatten.sandbox.catchAll {
          case Exit.Termination(_, exceptions, trace) =>
            F.fail(new IRTDecodingException(s"$methodId: Failed to decode JSON '${parsedBody.noSpaces}'.\nTrace: $trace", exceptions.headOption))
          case Exit.Error(decodingFailure, trace) =>
            F.fail(new IRTDecodingException(s"$methodId: Failed to decode JSON '${parsedBody.noSpaces}'.\nTrace: $trace", Some(decodingFailure)))
        }
        result  <- F.syncThrowable(method.invoke(context, requestBody.value.asInstanceOf[method.signature.Input])).flatten
        response = IRTResBody(result)
        encoded <- F.syncThrowable(method.marshaller.encodeResponse.apply(IRTResBody(result)))
        result  <- middleware.apply(methodId)(context, response, encoded)
      } yield result
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy