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)
}
}