Skip to content

Commit

Permalink
Merge pull request #264 from http4s/pr/i263
Browse files Browse the repository at this point in the history
Refresh `ServiceWorker` API
  • Loading branch information
armanbilge authored Apr 23, 2023
2 parents ccbf044 + 6e44f45 commit b60def9
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 11 deletions.
8 changes: 6 additions & 2 deletions dom/src/main/scala/org/http4s/dom/FetchEventContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ final class FetchEventContext[F[_]] private (
)

object FetchEventContext {
private[dom] val IOKey = Key.newKey[SyncIO, FetchEventContext[IO]].unsafeRunSync()
val IOKey: Key[FetchEventContext[IO]] =
Key.newKey[SyncIO, FetchEventContext[IO]].unsafeRunSync()

def apply(request: Request[IO]): IO[FetchEventContext[IO]] =
def fromRequest(request: Request[IO]): IO[FetchEventContext[IO]] =
IO.fromEither(request.attributes.lookup(IOKey).toRight(new NoSuchElementException))

@deprecated("Use fromRequest", "0.2.8")
def apply(request: Request[IO]): IO[FetchEventContext[IO]] = fromRequest(request)

private[dom] def apply[F[_]](event: FetchEvent, supervisor: Supervisor[F])(
implicit F: Async[F]): FetchEventContext[F] =
new FetchEventContext(
Expand Down
47 changes: 40 additions & 7 deletions dom/src/main/scala/org/http4s/dom/ServiceWorker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ package dom

import cats.data.Kleisli
import cats.data.OptionT
import cats.effect.Async
import cats.effect.IO
import cats.effect.SyncIO
import cats.effect.kernel.Async
import cats.effect.kernel.Deferred
import cats.effect.kernel.Resource
import cats.effect.std.Dispatcher
import cats.effect.std.Supervisor
import cats.effect.unsafe.IORuntime
import cats.syntax.all._
Expand All @@ -35,16 +37,47 @@ import org.scalajs.dom.ServiceWorkerGlobalScope
import org.scalajs.dom.{Response => DomResponse}
import org.typelevel.vault.Key

import scala.scalajs.js

object ServiceWorker {

/**
* Adds a listener for `FetchEvent`. The provided `IO` is run once to install the `routes`. If
* the event is not intercepted by `routes` then it is treated as an ordinary request.
* Additional context can be retrieved via [[FetchEventContext]] including a `Supervisor` for
* running background tasks.
* @return
* an action for removing the listener.
* Adds a listener for `FetchEvent`. If the event is not intercepted by `routes` then it is
* treated as an ordinary request. Additional context can be retrieved via
* [[FetchEventContext]] including a [[cats.effect.std.Supervisor]] for running background
* tasks after returning the response.
*/
def addFetchEventListener[F[_]](routes: HttpRoutes[F], contextKey: Key[FetchEventContext[F]])(
implicit F: Async[F]
): Resource[F, Unit] = Dispatcher.parallel.flatMap { dispatcher =>
val jsHandler: js.Function1[FetchEvent, Unit] = { event =>
event.respondWith {
dispatcher.unsafeToPromise {
Supervisor[F](await = true).allocated.flatMap {
case (supervisor, await) =>
val response = routesToListener(routes, supervisor, contextKey).run(event)
response.getOrElseF(F.fromPromise(F.delay(Fetch.fetch(event.request)))) <*
F.delay(event.waitUntil(dispatcher.unsafeToPromise(await)))
}
}
}
}

Resource.make {
F.delay(ServiceWorkerGlobalScope.self.addEventListener("fetch", jsHandler))
}(_ => F.delay(ServiceWorkerGlobalScope.self.removeEventListener("fetch", jsHandler)))
}

/**
* Adds a listener for `FetchEvent`. If the event is not intercepted by `routes` then it is
* treated as an ordinary request. Additional context can be retrieved via
* [[FetchEventContext]] including a [[cats.effect.std.Supervisor]] for running background
* tasks after returning the response.
*/
def addFetchEventListener(routes: HttpRoutes[IO]): Resource[IO, Unit] =
addFetchEventListener(routes, FetchEventContext.IOKey)

@deprecated("Use overload that directly takes routes and returns Resource", "0.2.8")
def addFetchEventListener(routes: IO[HttpRoutes[IO]])(
implicit runtime: IORuntime): SyncIO[SyncIO[Unit]] = for {
handler <- Deferred.in[SyncIO, IO, Either[Throwable, HttpRoutes[IO]]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package org.http4s.dom

import cats.effect.IO
import cats.effect.unsafe.implicits._
import org.scalajs.dom.ExtendableEvent
import org.scalajs.dom.ServiceWorkerGlobalScope
Expand All @@ -35,7 +34,7 @@ object TestServiceWorker {
)
)

ServiceWorker.addFetchEventListener(IO.pure(TestRoutes.routes)).void.unsafeRunSync()
ServiceWorker.addFetchEventListener(TestRoutes.routes).useForever.unsafeRunAndForget()
}

}

0 comments on commit b60def9

Please sign in to comment.