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

dev.cheleb.ziotapir.SameOriginBackendClient.scala Maven / Gradle / Ivy

package dev.cheleb.ziotapir

import zio.*
import zio.stream.*

import scala.scalajs.LinkingInfo
import scala.scalajs.js

import dev.cheleb.ziojwt.WithToken
import izumi.reflect.Tag
import org.scalajs.dom.window
import sttp.capabilities.zio.ZioStreams
import sttp.client3.*
import sttp.client3.impl.zio.FetchZioBackend
import sttp.model.Uri
import sttp.tapir.Endpoint
import sttp.tapir.client.sttp.SttpClientInterpreter

import laminar.Session

/** A client to the backend, extending the endpoints as methods.
  */
trait SameOriginBackendClient {

  /** Call an endpoint with a payload.
    *
    * This method turns an endpoint into a Task, that:
    *   - build a request from a payload
    *   - sends it to the backend
    *   - returns the response.
    *
    * @param endpoint
    * @param payload
    * @return
    */
  private[ziotapir] def requestZIO[I, E <: Throwable, O](
      endpoint: Endpoint[Unit, I, E, O, Any]
  )(
      payload: I
  ): Task[O]

  /** Call a secured endpoint with a payload.
    *
    * This method turns a secured endpoint into a Task, that:
    *   - build a request from a payload and a security token
    *   - sends it to the backend
    *   - returns the response.
    *
    * @param endpoint
    * @param payload
    * @return
    */
  private[ziotapir] def securedRequestZIO[
      UserToken <: WithToken,
      I,
      E <: Throwable,
      O
  ](
      endpoint: Endpoint[String, I, E, O, Any]
  )(payload: I)(using session: Session[UserToken]): Task[O]

  private[ziotapir] def streamRequestZIO[I, O](
      endpoint: Endpoint[Unit, I, Throwable, Stream[Throwable, O], ZioStreams]
  )(payload: I): Task[Stream[Throwable, O]]

  private[ziotapir] def securedStreamRequestZIO[UserToken <: WithToken, I, O](
      endpoint: Endpoint[String, I, Throwable, Stream[Throwable, O], ZioStreams]
  )(payload: I)(using session: Session[UserToken]): Task[Stream[Throwable, O]]

}

/** A client to the backend, extending the endpoints as methods.
  */

/** The live implementation of the BackendClient.
  *
  * @param backend
  * @param interpreter
  * @param config
  */
private class SameOriginBackendClientLive(
    backend: SttpBackend[Task, ZioStreamsWithWebSockets],
    interpreter: SttpClientInterpreter,
    config: BackendClientConfig
) extends BackendClient(backend, interpreter)
    with SameOriginBackendClient {

  /** Call an endpoint with a payload.
    * @param endpoint
    * @param payload
    * @return
    */

  private[ziotapir] def requestZIO[I, E <: Throwable, O](
      endpoint: Endpoint[Unit, I, E, O, Any]
  )(
      payload: I
  ): ZIO[Any, Throwable, O] =
    requestZIO(config.baseUrl, endpoint)(payload)

    /** Call a secured endpoint with a payload.
      * @param endpoint
      * @param payload
      * @return
      */
  private[ziotapir] def securedRequestZIO[
      UserToken <: WithToken,
      I,
      E <: Throwable,
      O
  ](
      endpoint: Endpoint[String, I, E, O, Any]
  )(payload: I)(using session: Session[UserToken]): ZIO[Any, Throwable, O] =
    securedRequestZIO(config.baseUrl, endpoint)(payload)

  private[ziotapir] def streamRequestZIO[I, O](
      endpoint: Endpoint[Unit, I, Throwable, Stream[Throwable, O], ZioStreams]
  )(payload: I): Task[Stream[Throwable, O]] =
    streamRequestZIO(config.baseUrl, endpoint)(payload)

  private[ziotapir] def securedStreamRequestZIO[UserToken <: WithToken, I, O](
      endpoint: Endpoint[String, I, Throwable, Stream[Throwable, O], ZioStreams]
  )(payload: I)(using session: Session[UserToken]): Task[Stream[Throwable, O]] =
    securedStreamRequestZIO(config.baseUrl, endpoint)(payload)

}

/** The live implementation of the BackendClient.
  */
object SameOriginBackendClientLive {

  /** The base URL of the backend in development mode. It is the value of the
    */
  private def developmentApiServer =
    if js.typeOf(js.Dynamic.global.DEV_API_URL) == "string"
    then Uri.unsafeParse(js.Dynamic.global.DEV_API_URL.toString)
    else Uri.unsafeParse("http://localhost:8080")

  /** The base URL of the backend. It is the origin of the current page in
    * production mode, and localhost:8080 or the DEV_API_URL environment
    * variable in development mode.
    * {{{
    * DEV_API_URL ="http://localhost:9999/";
    * }}}
    */
  lazy val backendBaseURL =
    if LinkingInfo.developmentMode then developmentApiServer
    else Uri.unsafeParse(window.document.location.origin)

    /** The layer that can be used to create a client.
      */
  private def layer: ZLayer[
    SttpBackend[Task, ZioStreamsWithWebSockets] &
      (SttpClientInterpreter & BackendClientConfig),
    Nothing,
    SameOriginBackendClientLive
  ] =
    ZLayer.derive[SameOriginBackendClientLive]

  /** The layer that can be used to create
    */
  private[ziotapir] def configuredLayer
      : ZLayer[Any, Nothing, SameOriginBackendClientLive] = {
    val backend: SttpBackend[Task, ZioStreamsWithWebSockets] = FetchZioBackend()
    val interpreter = SttpClientInterpreter()
    val config = BackendClientConfig(backendBaseURL)

    ZLayer.succeed(backend) ++ ZLayer.succeed(interpreter) ++ ZLayer.succeed(
      config
    ) >>> layer
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy