mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Hooking the new pubsub impl into the application
This commit is contained in:
@ -6,8 +6,6 @@
|
||||
|
||||
package docspell.backend
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
import cats.effect._
|
||||
|
||||
import docspell.backend.auth.Login
|
||||
@ -15,15 +13,13 @@ import docspell.backend.fulltext.CreateIndex
|
||||
import docspell.backend.ops._
|
||||
import docspell.backend.signup.OSignup
|
||||
import docspell.ftsclient.FtsClient
|
||||
import docspell.joexapi.client.JoexClient
|
||||
import docspell.pubsub.api.PubSubT
|
||||
import docspell.store.Store
|
||||
import docspell.store.queue.JobQueue
|
||||
import docspell.store.usertask.UserTaskStore
|
||||
import docspell.totp.Totp
|
||||
|
||||
import emil.javamail.{JavaMailEmil, Settings}
|
||||
import org.http4s.blaze.client.BlazeClientBuilder
|
||||
import org.http4s.client.Client
|
||||
|
||||
trait BackendApp[F[_]] {
|
||||
|
||||
@ -49,6 +45,7 @@ trait BackendApp[F[_]] {
|
||||
def clientSettings: OClientSettings[F]
|
||||
def totp: OTotp[F]
|
||||
def share: OShare[F]
|
||||
def pubSub: PubSubT[F]
|
||||
}
|
||||
|
||||
object BackendApp {
|
||||
@ -56,8 +53,8 @@ object BackendApp {
|
||||
def create[F[_]: Async](
|
||||
cfg: Config,
|
||||
store: Store[F],
|
||||
httpClient: Client[F],
|
||||
ftsClient: FtsClient[F]
|
||||
ftsClient: FtsClient[F],
|
||||
pubSubT: PubSubT[F]
|
||||
): Resource[F, BackendApp[F]] =
|
||||
for {
|
||||
utStore <- UserTaskStore(store)
|
||||
@ -65,7 +62,7 @@ object BackendApp {
|
||||
totpImpl <- OTotp(store, Totp.default)
|
||||
loginImpl <- Login[F](store, Totp.default)
|
||||
signupImpl <- OSignup[F](store)
|
||||
joexImpl <- OJoex(JoexClient(httpClient), store)
|
||||
joexImpl <- OJoex(pubSubT)
|
||||
collImpl <- OCollective[F](store, utStore, queue, joexImpl)
|
||||
sourceImpl <- OSource[F](store)
|
||||
tagImpl <- OTag[F](store)
|
||||
@ -90,6 +87,7 @@ object BackendApp {
|
||||
OShare(store, itemSearchImpl, simpleSearchImpl, javaEmil)
|
||||
)
|
||||
} yield new BackendApp[F] {
|
||||
val pubSub = pubSubT
|
||||
val login = loginImpl
|
||||
val signup = signupImpl
|
||||
val collective = collImpl
|
||||
@ -113,15 +111,4 @@ object BackendApp {
|
||||
val totp = totpImpl
|
||||
val share = shareImpl
|
||||
}
|
||||
|
||||
def apply[F[_]: Async](
|
||||
cfg: Config,
|
||||
connectEC: ExecutionContext
|
||||
)(ftsFactory: Client[F] => Resource[F, FtsClient[F]]): Resource[F, BackendApp[F]] =
|
||||
for {
|
||||
store <- Store.create(cfg.jdbc, cfg.files.chunkSize, connectEC)
|
||||
httpClient <- BlazeClientBuilder[F].resource
|
||||
ftsClient <- ftsFactory(httpClient)
|
||||
backend <- create(cfg, store, httpClient, ftsClient)
|
||||
} yield backend
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2020 Eike K. & Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package docspell.backend.msg
|
||||
|
||||
import docspell.common._
|
||||
import docspell.pubsub.api.{Topic, TypedTopic}
|
||||
|
||||
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
|
||||
import io.circe.{Decoder, Encoder}
|
||||
|
||||
/** Message to request to cancel a job. */
|
||||
final case class CancelJob(jobId: Ident, nodeId: Ident)
|
||||
|
||||
object CancelJob {
|
||||
implicit val jsonDecoder: Decoder[CancelJob] =
|
||||
deriveDecoder[CancelJob]
|
||||
|
||||
implicit val jsonEncoder: Encoder[CancelJob] =
|
||||
deriveEncoder[CancelJob]
|
||||
|
||||
val topic: TypedTopic[CancelJob] =
|
||||
TypedTopic(Topic("job-cancel-request"))
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020 Eike K. & Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package docspell.backend.msg
|
||||
|
||||
import docspell.common._
|
||||
import docspell.pubsub.api.{Topic, TypedTopic}
|
||||
|
||||
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
|
||||
import io.circe.{Decoder, Encoder}
|
||||
|
||||
/** Message to notify about finished jobs. They have a final state. */
|
||||
final case class JobDone(jobId: Ident, task: Ident, args: String, state: JobState)
|
||||
object JobDone {
|
||||
implicit val jsonDecoder: Decoder[JobDone] =
|
||||
deriveDecoder[JobDone]
|
||||
|
||||
implicit val jsonEncoder: Encoder[JobDone] =
|
||||
deriveEncoder[JobDone]
|
||||
|
||||
val topic: TypedTopic[JobDone] =
|
||||
TypedTopic(Topic("job-finished"))
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2020 Eike K. & Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package docspell.backend.msg
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
import docspell.pubsub.api.{Topic, TypedTopic}
|
||||
|
||||
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
|
||||
import io.circe.{Decoder, Encoder}
|
||||
|
||||
final case class Ping(sender: String, num: Long)
|
||||
|
||||
object Ping {
|
||||
implicit val jsonDecoder: Decoder[Ping] =
|
||||
deriveDecoder[Ping]
|
||||
|
||||
implicit val jsonEncoder: Encoder[Ping] =
|
||||
deriveEncoder[Ping]
|
||||
|
||||
private[this] val counter = new AtomicLong(0)
|
||||
def next(sender: String): Ping =
|
||||
Ping(sender, counter.getAndIncrement())
|
||||
|
||||
val topic: TypedTopic[Ping] =
|
||||
TypedTopic[Ping](Topic("ping"))
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2020 Eike K. & Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package docspell.backend.msg
|
||||
|
||||
import cats.data.NonEmptyList
|
||||
|
||||
import docspell.pubsub.api.{Topic, TypedTopic}
|
||||
|
||||
/** All topics used in Docspell. */
|
||||
object Topics {
|
||||
|
||||
/** A generic notification to the job executors to look for new work. */
|
||||
val jobsNotify: TypedTopic[Unit] =
|
||||
TypedTopic[Unit](Topic("jobs-notify"))
|
||||
|
||||
/** A list of all topics. It is required to list every topic in use here! */
|
||||
val all: NonEmptyList[TypedTopic[_]] =
|
||||
NonEmptyList.of(Ping.topic, JobDone.topic, CancelJob.topic, jobsNotify)
|
||||
}
|
@ -11,8 +11,7 @@ import cats.effect._
|
||||
import cats.implicits._
|
||||
|
||||
import docspell.backend.ops.OJob.{CollectiveQueueState, JobCancelResult}
|
||||
import docspell.common.Priority
|
||||
import docspell.common.{Ident, JobState}
|
||||
import docspell.common._
|
||||
import docspell.store.Store
|
||||
import docspell.store.UpdateResult
|
||||
import docspell.store.queries.QJob
|
||||
@ -55,6 +54,7 @@ object OJob {
|
||||
joex: OJoex[F]
|
||||
): Resource[F, OJob[F]] =
|
||||
Resource.pure[F, OJob[F]](new OJob[F] {
|
||||
private[this] val logger = Logger.log4s(org.log4s.getLogger(OJob.getClass))
|
||||
|
||||
def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState] =
|
||||
store
|
||||
@ -77,11 +77,9 @@ object OJob {
|
||||
job.worker match {
|
||||
case Some(worker) =>
|
||||
for {
|
||||
flag <- joex.cancelJob(job.id, worker)
|
||||
res <-
|
||||
if (flag) JobCancelResult.cancelRequested.pure[F]
|
||||
else remove(job)
|
||||
} yield res
|
||||
_ <- logger.debug(s"Attempt to cancel job: ${job.id.id}")
|
||||
_ <- joex.cancelJob(job.id, worker)
|
||||
} yield JobCancelResult.cancelRequested
|
||||
case None =>
|
||||
remove(job)
|
||||
}
|
||||
|
@ -6,41 +6,27 @@
|
||||
|
||||
package docspell.backend.ops
|
||||
|
||||
import cats.data.OptionT
|
||||
import cats.effect._
|
||||
import cats.implicits._
|
||||
|
||||
import docspell.common.{Ident, NodeType}
|
||||
import docspell.joexapi.client.JoexClient
|
||||
import docspell.store.Store
|
||||
import docspell.store.records.RNode
|
||||
import docspell.backend.msg.{CancelJob, Topics}
|
||||
import docspell.common.Ident
|
||||
import docspell.pubsub.api.PubSubT
|
||||
|
||||
trait OJoex[F[_]] {
|
||||
|
||||
def notifyAllNodes: F[Unit]
|
||||
|
||||
def cancelJob(job: Ident, worker: Ident): F[Boolean]
|
||||
|
||||
def cancelJob(job: Ident, worker: Ident): F[Unit]
|
||||
}
|
||||
|
||||
object OJoex {
|
||||
|
||||
def apply[F[_]: Sync](client: JoexClient[F], store: Store[F]): Resource[F, OJoex[F]] =
|
||||
def apply[F[_]](pubSub: PubSubT[F]): Resource[F, OJoex[F]] =
|
||||
Resource.pure[F, OJoex[F]](new OJoex[F] {
|
||||
|
||||
def notifyAllNodes: F[Unit] =
|
||||
for {
|
||||
nodes <- store.transact(RNode.findAll(NodeType.Joex))
|
||||
_ <- nodes.toList.traverse(n => client.notifyJoexIgnoreErrors(n.url))
|
||||
} yield ()
|
||||
pubSub.publish1IgnoreErrors(Topics.jobsNotify, ())
|
||||
|
||||
def cancelJob(job: Ident, worker: Ident): F[Boolean] =
|
||||
(for {
|
||||
node <- OptionT(store.transact(RNode.findById(worker)))
|
||||
cancel <- OptionT.liftF(client.cancelJob(node.url, job))
|
||||
} yield cancel.success).getOrElse(false)
|
||||
def cancelJob(job: Ident, worker: Ident): F[Unit] =
|
||||
pubSub.publish1IgnoreErrors(CancelJob.topic, CancelJob(job, worker))
|
||||
})
|
||||
|
||||
def create[F[_]: Async](store: Store[F]): Resource[F, OJoex[F]] =
|
||||
JoexClient.resource.flatMap(client => apply(client, store))
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user