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

caliban.ws.WebSocketHooks.scala Maven / Gradle / Ivy

The newest version!
package caliban.ws

import caliban.{ GraphQLWSOutput, InputValue, ResponseValue }
import zio.ZIO
import zio.stream.ZPipeline

trait WebSocketHooks[-R, +E] { self =>
  def beforeInit: Option[InputValue => ZIO[R, E, Any]]                       = None
  def afterInit: Option[ZIO[R, E, Any]]                                      = None
  def onMessage: Option[ZPipeline[R, E, GraphQLWSOutput, GraphQLWSOutput]]   = None
  def onPong: Option[InputValue => ZIO[R, E, Any]]                           = None
  def onPing: Option[Option[InputValue] => ZIO[R, E, Option[ResponseValue]]] = None
  def onAck: Option[ZIO[R, E, ResponseValue]]                                = None

  def ++[R2 <: R, E2 >: E](other: WebSocketHooks[R2, E2]): WebSocketHooks[R2, E2] =
    new WebSocketHooks[R2, E2] {
      override def beforeInit: Option[InputValue => ZIO[R2, E2, Any]] = (self.beforeInit, other.beforeInit) match {
        case (None, Some(f))      => Some(f)
        case (Some(f), None)      => Some(f)
        case (Some(f1), Some(f2)) => Some((x: InputValue) => f1(x) *> f2(x))
        case _                    => None
      }

      override def afterInit: Option[ZIO[R2, E2, Any]] = (self.afterInit, other.afterInit) match {
        case (None, Some(f))      => Some(f)
        case (Some(f), None)      => Some(f)
        case (Some(f1), Some(f2)) => Some(f1 &> f2)
        case _                    => None
      }

      override def onMessage: Option[ZPipeline[R2, E2, GraphQLWSOutput, GraphQLWSOutput]] =
        (self.onMessage, other.onMessage) match {
          case (None, Some(f))      => Some(f)
          case (Some(f), None)      => Some(f)
          case (Some(f1), Some(f2)) => Some(f1.andThen(f2))
          case _                    => None
        }

      override def onPong: Option[InputValue => ZIO[R2, E2, Any]] = (self.onPong, other.onPong) match {
        case (None, Some(f))      => Some(f)
        case (Some(f), None)      => Some(f)
        case (Some(f1), Some(f2)) => Some((x: InputValue) => f1(x) &> f2(x))
        case _                    => None
      }

      override def onPing: Option[Option[InputValue] => ZIO[R2, E2, Option[ResponseValue]]] =
        (self.onPing, other.onPing) match {
          case (None, Some(f))      => Some(f)
          case (Some(f), None)      => Some(f)
          case (Some(f1), Some(f2)) =>
            Some { (x: Option[InputValue]) =>
              f1(x).zipWithPar(f2(x)) {
                case (a @ Some(_), None) => a
                case (None, b @ Some(_)) => b
                case (Some(a), Some(b))  => Some(a.deepMerge(b))
                case _                   => None
              }
            }
          case _                    => None
        }

      override def onAck: Option[ZIO[R2, E2, ResponseValue]] = (self.onAck, other.onAck) match {
        case (None, Some(f))      => Some(f)
        case (Some(f), None)      => Some(f)
        case (Some(f1), Some(f2)) => Some((f1 zipWithPar f2)(_ deepMerge _))
        case _                    => None
      }
    }
}

object WebSocketHooks {
  def empty[R, E]: WebSocketHooks[R, E] = Empty

  private case object Empty extends WebSocketHooks[Any, Nothing]

  /**
   * Specifies a callback that will be run before an incoming subscription
   * request is accepted. Useful for e.g authorizing the incoming subscription
   * before accepting it.
   */
  def init[R, E](f: InputValue => ZIO[R, E, Any]): WebSocketHooks[R, E] =
    new WebSocketHooks[R, E] {
      override def beforeInit: Option[InputValue => ZIO[R, E, Any]] = Some(f)
    }

  /**
   * Specifies a callback that will be run after an incoming subscription
   * request has been accepted. Useful for e.g terminating a subscription
   * after some time, such as authorization expiring.
   */
  def afterInit[R, E](f: ZIO[R, E, Any]): WebSocketHooks[R, E] =
    new WebSocketHooks[R, E] {
      override def afterInit: Option[ZIO[R, E, Any]] = Some(f)
    }

  /**
   * Specifies a ZPipeline that will be applied to the resulting `ZStream`
   * for every active subscription. Useful to e.g modify the environment
   * to inject session information into the `ZStream` handling the
   * subscription.
   */
  def message[R, E](f: ZPipeline[R, E, GraphQLWSOutput, GraphQLWSOutput]): WebSocketHooks[R, E] =
    new WebSocketHooks[R, E] {
      override def onMessage: Option[ZPipeline[R, E, GraphQLWSOutput, GraphQLWSOutput]] = Some(f)
    }

  /**
   * Specifies a callback that will be run when ever a pong message is received.
   */
  def pong[R, E](f: InputValue => ZIO[R, E, Any]): WebSocketHooks[R, E] =
    new WebSocketHooks[R, E] {
      override def onPong: Option[InputValue => ZIO[R, E, Any]] = Some(f)
    }

  def ack[R, E](f: ZIO[R, E, ResponseValue]): WebSocketHooks[R, E] =
    new WebSocketHooks[R, E] {
      override def onAck: Option[ZIO[R, E, ResponseValue]] = Some(f)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy