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

dev.cheleb.ziolaminartapir.package.scala Maven / Gradle / Ivy

package dev.cheleb.ziolaminartapir

import zio.*

import scala.annotation.targetName

import com.raquo.laminar.api.L.*
import sttp.tapir.Endpoint
import dev.cheleb.ziojwt.WithToken
import sttp.model.Uri
import sttp.capabilities.zio.ZioStreams
import sttp.capabilities.WebSockets

/** Extension methods for ZIO JS.
  *
  * Extension are of two kinds:
  *   - targeting Endpoints: allowing to call an endpoint with a payload
  *   - targeting ZIO: allowing to run a ZIO in JS
  *
  * Also, there are two kinds of ZIO, and associated 2 extensions:
  *   - SameOriginBackendClient: for requests to the same origin
  *   - DifferentOriginBackendClient: for requests to a different origin
  */

type ZioStreamsWithWebSockets = ZioStreams & WebSockets

/** Typed exception for restricted endpoints.
  * @param message
  */
case class RestrictedEndpointException(message: String)
    extends RuntimeException(message)

/** ZIO JS extension methods.
  *
  * This object contains:
  *   - convenience methods for calling endpoints.
  *   - extension methods for ZIO that are specific to the Laminar JS
  *     environment.
  */

/** Extension to ZIO[SameOriginBackendClient, E, A] that allows us to run in JS.
  */
extension [E <: Throwable, A](zio: ZIO[SameOriginBackendClient, E, A])

  /** Run the underlying request to the default backend.
    */
  private def exec: Unit =
    Unsafe.unsafe { implicit unsafe =>
      Runtime.default.unsafe.fork(
        zio.provide(SameOriginBackendClientLive.configuredLayer)
      )
    }

  /** Run the ZIO in JS.
    *
    * @return
    */
  def runJs: Unit =
    zio
      .tapError(th => Console.printLineError(th.getMessage()))
      .exec

  /** Run the ZIO in JS, and emit the error to an EventBus.
    * @param errorBus
    *   the event bus to emit the error to
    */
  def runJs(errorBus: EventBus[E]): Unit =
    zio
      .tapError(e => ZIO.attempt(errorBus.emit(e)))
      .exec

  /** Emit the result of the ZIO to an EventBus.
    *
    * Underlying request to the default backend.
    * @param bus
    */
  def emitTo(bus: EventBus[A]): Unit =
    zio
      .tapError(th => Console.printLineError(th.getMessage()))
      .tap(a => ZIO.attempt(bus.emit(a)))
      .exec

  /** Emit the result and error of the ZIO to an EventBus.
    *
    * Underlying request to the default backend.
    *
    * @param bus
    * @param error
    */
  def emitTo(
      bus: EventBus[A],
      error: EventBus[E]
  ): Unit =
    zio
      .tapError(e => ZIO.attempt(error.emit(e)))
      .tap(a => ZIO.attempt(bus.emit(a)))
      .exec

  /** Emit the result of the ZIO to an EventBus of Either.
    *
    * Underlying request to the default backend.
    *
    * @param bus
    *   event bus to emit the result to
    */
  def emitToEither(
      bus: EventBus[Either[E, A]]
  ): Unit =
    zio
      .tapError(e => ZIO.attempt(bus.emit(Left(e))))
      .tap(a => ZIO.attempt(bus.emit(Right(a))))
      .exec

  /** Emit the result of the ZIO to an EventBus, and return the EventStream.
    */
  def toEventStream: EventStream[A] = {
    val eventBus = EventBus[A]()
    emitTo(eventBus)
    eventBus.events
  }

/** Extension to ZIO[DifferentOriginBackendClient, E, A] that allows us to run
  * in JS.
  */
extension [E <: Throwable, A](zio: ZIO[DifferentOriginBackendClient, E, A])
  /** Run the underlying request to the default backend.
    */
  @targetName("dexec")
  private def exec: Unit =
    Unsafe.unsafe { implicit unsafe =>
      Runtime.default.unsafe.fork(
        zio.provide(DifferentOriginBackendClientLive.configuredLayer)
      )
    }

  /** Run the ZIO in JS.
    *
    * @return
    */
  @targetName("drunJs")
  def runJs: Unit =
    zio
      .tapError(th => Console.printLineError(th.getMessage()))
      .exec

  /** Run the ZIO in JS, and emit the error to an EventBus.
    * @param errorBus
    *   the event bus to emit the error to
    */
  @targetName("drunJs")
  def runJs(errorBus: EventBus[E]): Unit =
    zio
      .tapError(e => ZIO.attempt(errorBus.emit(e)))
      .exec

  /** Emit the result of the ZIO to an EventBus.
    *
    * Underlying request to the default backend.
    * @param bus
    */
  @targetName("demitTo")
  def emitTo(bus: EventBus[A]): Unit =
    zio
      .tapError(th => Console.printLineError(th.getMessage()))
      .tap(a => ZIO.attempt(bus.emit(a)))
      .exec

  /** Emit the result and error of the ZIO to an EventBus.
    *
    * Underlying request to the default backend.
    *
    * @param bus
    * @param error
    */
  @targetName("demitTo")
  def emitTo(
      bus: EventBus[A],
      error: EventBus[E]
  ): Unit =
    zio
      .tapError(e => ZIO.attempt(error.emit(e)))
      .tap(a => ZIO.attempt(bus.emit(a)))
      .exec

  /** Emit the result of the ZIO to an EventBus of Either.
    *
    * Underlying request to the default backend.
    *
    * @param bus
    *   event bus to emit the result to
    */
  @targetName("demitToEither")
  def emitToEither(
      bus: EventBus[Either[E, A]]
  ): Unit =
    zio
      .tapError(e => ZIO.attempt(bus.emit(Left(e))))
      .tap(a => ZIO.attempt(bus.emit(Right(a))))
      .exec

  /** Emit the result of the ZIO to an EventBus, and return the EventStream.
    */
  @targetName("dtoEventStream")
  def toEventStream: EventStream[A] = {
    val eventBus = EventBus[A]()
    emitTo(eventBus)
    eventBus.events
  }

/** Extension that allows us to turn an unsecure endpoint to a function from a
  * payload to a ZIO.
  */
extension [I, E <: Throwable, O](
    endpoint: Endpoint[Unit, I, E, O, Any]
)
  /** Call the endpoint with a payload, and get a ZIO back.
    * @param payload
    * @return
    */
  def apply(payload: I): RIO[SameOriginBackendClient, O] =
    ZIO
      .service[SameOriginBackendClient]
      .flatMap(_.endpointRequestZIO(endpoint)(payload))

  /** Call the endpoint with a payload on a different backend than the origin,
    * and get a ZIO back.
    */
  @targetName("dapply")
  def on(baseUri: Uri)(payload: I): RIO[DifferentOriginBackendClient, O] =
    ZIO
      .service[DifferentOriginBackendClient]
      .flatMap(_.endpointRequestZIO(baseUri, endpoint)(payload))

/** Extension that allows us to turn a secured endpoint to a function from a
  * payload to a ZIO.
  */
extension [I, E <: Throwable, O](endpoint: Endpoint[String, I, E, O, Any])
  /** Call the secured endpoint with a payload, and get a ZIO back.
    * @param payload
    * @return
    */
  @targetName("securedApply")
  def apply[UserToken <: WithToken](
      payload: I
  )(using session: Session[UserToken]): RIO[SameOriginBackendClient, O] =
    ZIO
      .service[SameOriginBackendClient]
      .flatMap(_.securedEndpointRequestZIO(endpoint)(payload))

  /** Call the secured endpoint with a payload on a different backend than the
    * origin, and get a ZIO back.
    */
  def on[UserToken <: WithToken](baseUri: Uri)(
      payload: I
  )(using session: Session[UserToken]): RIO[DifferentOriginBackendClient, O] =
    ZIO
      .service[DifferentOriginBackendClient]
      .flatMap(_.securedEndpointRequestZIO(baseUri, endpoint)(payload))




© 2015 - 2025 Weber Informatics LLC | Privacy Policy