mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Adopt modules to new collective table
This commit is contained in:
@ -26,7 +26,7 @@ trait EventContext {
|
|||||||
"account" -> Json.obj(
|
"account" -> Json.obj(
|
||||||
"collective" -> event.account.collective.asJson,
|
"collective" -> event.account.collective.asJson,
|
||||||
"user" -> event.account.login.asJson,
|
"user" -> event.account.login.asJson,
|
||||||
"login" -> event.account.asJson
|
"login" -> event.account.asAccountId.asJson
|
||||||
),
|
),
|
||||||
"content" -> content
|
"content" -> content
|
||||||
)
|
)
|
||||||
|
@ -70,7 +70,7 @@ object BasicData {
|
|||||||
|
|
||||||
def find(
|
def find(
|
||||||
itemIds: NonEmptyList[Ident],
|
itemIds: NonEmptyList[Ident],
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
now: Timestamp
|
now: Timestamp
|
||||||
): ConnectionIO[Vector[Item]] = {
|
): ConnectionIO[Vector[Item]] = {
|
||||||
import ItemQueryDsl._
|
import ItemQueryDsl._
|
||||||
|
@ -46,7 +46,7 @@ object DeleteFieldValueCtx {
|
|||||||
for {
|
for {
|
||||||
now <- OptionT.liftF(Timestamp.current[ConnectionIO])
|
now <- OptionT.liftF(Timestamp.current[ConnectionIO])
|
||||||
items <- OptionT.liftF(Item.find(ev.items, ev.account, now))
|
items <- OptionT.liftF(Item.find(ev.items, ev.account, now))
|
||||||
field <- OptionT(RCustomField.findById(ev.field, ev.account.collective))
|
field <- OptionT(RCustomField.findById(ev.field, ev.account.collectiveId))
|
||||||
msg = DeleteFieldValueCtx(
|
msg = DeleteFieldValueCtx(
|
||||||
ev,
|
ev,
|
||||||
Data(
|
Data(
|
||||||
@ -71,7 +71,7 @@ object DeleteFieldValueCtx {
|
|||||||
)
|
)
|
||||||
|
|
||||||
final case class Data(
|
final case class Data(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
items: List[Item],
|
items: List[Item],
|
||||||
field: Field,
|
field: Field,
|
||||||
itemUrl: Option[String]
|
itemUrl: Option[String]
|
||||||
|
@ -61,7 +61,7 @@ object ItemSelectionCtx {
|
|||||||
items.toList,
|
items.toList,
|
||||||
ev.itemUrl,
|
ev.itemUrl,
|
||||||
ev.more,
|
ev.more,
|
||||||
ev.account.user.id
|
ev.account.login.id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} yield msg
|
} yield msg
|
||||||
@ -73,12 +73,12 @@ object ItemSelectionCtx {
|
|||||||
items <- ev.items.traverse(Item.sample[F])
|
items <- ev.items.traverse(Item.sample[F])
|
||||||
} yield ItemSelectionCtx(
|
} yield ItemSelectionCtx(
|
||||||
ev,
|
ev,
|
||||||
Data(ev.account, items.toList, ev.itemUrl, ev.more, ev.account.user.id)
|
Data(ev.account, items.toList, ev.itemUrl, ev.more, ev.account.login.id)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
final case class Data(
|
final case class Data(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
items: List[Item],
|
items: List[Item],
|
||||||
itemUrl: Option[String],
|
itemUrl: Option[String],
|
||||||
more: Boolean,
|
more: Boolean,
|
||||||
@ -89,7 +89,7 @@ object ItemSelectionCtx {
|
|||||||
io.circe.generic.semiauto.deriveEncoder
|
io.circe.generic.semiauto.deriveEncoder
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
items: Vector[ListItem],
|
items: Vector[ListItem],
|
||||||
baseUrl: Option[LenientUri],
|
baseUrl: Option[LenientUri],
|
||||||
more: Boolean,
|
more: Boolean,
|
||||||
@ -100,7 +100,7 @@ object ItemSelectionCtx {
|
|||||||
items.map(Item(now)).toList,
|
items.map(Item(now)).toList,
|
||||||
baseUrl.map(_.asString),
|
baseUrl.map(_.asString),
|
||||||
more,
|
more,
|
||||||
account.user.id
|
account.login.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ object SetFieldValueCtx {
|
|||||||
for {
|
for {
|
||||||
now <- OptionT.liftF(Timestamp.current[ConnectionIO])
|
now <- OptionT.liftF(Timestamp.current[ConnectionIO])
|
||||||
items <- OptionT.liftF(Item.find(ev.items, ev.account, now))
|
items <- OptionT.liftF(Item.find(ev.items, ev.account, now))
|
||||||
field <- OptionT(RCustomField.findById(ev.field, ev.account.collective))
|
field <- OptionT(RCustomField.findById(ev.field, ev.account.collectiveId))
|
||||||
msg = SetFieldValueCtx(
|
msg = SetFieldValueCtx(
|
||||||
ev,
|
ev,
|
||||||
Data(
|
Data(
|
||||||
@ -70,7 +70,7 @@ object SetFieldValueCtx {
|
|||||||
)
|
)
|
||||||
|
|
||||||
final case class Data(
|
final case class Data(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
items: List[Item],
|
items: List[Item],
|
||||||
field: Field,
|
field: Field,
|
||||||
value: String,
|
value: String,
|
||||||
|
@ -39,8 +39,8 @@ object TagsChangedCtx {
|
|||||||
def apply: Factory =
|
def apply: Factory =
|
||||||
EventContext.factory(ev =>
|
EventContext.factory(ev =>
|
||||||
for {
|
for {
|
||||||
tagsAdded <- RTag.findAllByNameOrId(ev.added, ev.account.collective)
|
tagsAdded <- RTag.findAllByNameOrId(ev.added, ev.account.collectiveId)
|
||||||
tagsRemov <- RTag.findAllByNameOrId(ev.removed, ev.account.collective)
|
tagsRemov <- RTag.findAllByNameOrId(ev.removed, ev.account.collectiveId)
|
||||||
now <- Timestamp.current[ConnectionIO]
|
now <- Timestamp.current[ConnectionIO]
|
||||||
items <- Item.find(ev.items, ev.account, now)
|
items <- Item.find(ev.items, ev.account, now)
|
||||||
msg = TagsChangedCtx(
|
msg = TagsChangedCtx(
|
||||||
@ -69,7 +69,7 @@ object TagsChangedCtx {
|
|||||||
)
|
)
|
||||||
|
|
||||||
final case class Data(
|
final case class Data(
|
||||||
account: AccountId,
|
account: AccountInfo,
|
||||||
items: List[Item],
|
items: List[Item],
|
||||||
added: List[Tag],
|
added: List[Tag],
|
||||||
removed: List[Tag],
|
removed: List[Tag],
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package docspell.scheduler
|
||||||
|
|
||||||
|
import cats.Applicative
|
||||||
|
import docspell.common.AccountInfo
|
||||||
|
|
||||||
|
/** Strategy to find the user that submitted the job. This is used to emit events about
|
||||||
|
* starting/finishing jobs.
|
||||||
|
*
|
||||||
|
* If an account cannot be determined, no events can be send.
|
||||||
|
*/
|
||||||
|
trait FindJobOwner[F[_]] {
|
||||||
|
def apply(job: Job[_]): F[Option[AccountInfo]]
|
||||||
|
}
|
||||||
|
|
||||||
|
object FindJobOwner {
|
||||||
|
|
||||||
|
def none[F[_]: Applicative]: FindJobOwner[F] =
|
||||||
|
(_: Job[_]) => Applicative[F].pure(None)
|
||||||
|
}
|
@ -17,7 +17,8 @@ import docspell.store.Store
|
|||||||
case class JobStoreModuleBuilder[F[_]: Async](
|
case class JobStoreModuleBuilder[F[_]: Async](
|
||||||
store: Store[F],
|
store: Store[F],
|
||||||
pubsub: PubSubT[F],
|
pubsub: PubSubT[F],
|
||||||
eventSink: EventSink[F]
|
eventSink: EventSink[F],
|
||||||
|
findJobOwner: FindJobOwner[F]
|
||||||
) {
|
) {
|
||||||
def withPubsub(ps: PubSubT[F]): JobStoreModuleBuilder[F] =
|
def withPubsub(ps: PubSubT[F]): JobStoreModuleBuilder[F] =
|
||||||
copy(pubsub = ps)
|
copy(pubsub = ps)
|
||||||
@ -25,8 +26,11 @@ case class JobStoreModuleBuilder[F[_]: Async](
|
|||||||
def withEventSink(es: EventSink[F]): JobStoreModuleBuilder[F] =
|
def withEventSink(es: EventSink[F]): JobStoreModuleBuilder[F] =
|
||||||
copy(eventSink = es)
|
copy(eventSink = es)
|
||||||
|
|
||||||
|
def withFindJobOwner(f: FindJobOwner[F]): JobStoreModuleBuilder[F] =
|
||||||
|
copy(findJobOwner = f)
|
||||||
|
|
||||||
def build: JobStoreModuleBuilder.Module[F] = {
|
def build: JobStoreModuleBuilder.Module[F] = {
|
||||||
val jobStore = JobStorePublish(store, pubsub, eventSink)
|
val jobStore = JobStorePublish(store, pubsub, eventSink, findJobOwner)
|
||||||
val periodicTaskStore = PeriodicTaskStore(store, jobStore)
|
val periodicTaskStore = PeriodicTaskStore(store, jobStore)
|
||||||
val userTaskStore = UserTaskStoreImpl(store, periodicTaskStore)
|
val userTaskStore = UserTaskStoreImpl(store, periodicTaskStore)
|
||||||
new JobStoreModuleBuilder.Module(
|
new JobStoreModuleBuilder.Module(
|
||||||
@ -35,7 +39,8 @@ case class JobStoreModuleBuilder[F[_]: Async](
|
|||||||
jobStore,
|
jobStore,
|
||||||
store,
|
store,
|
||||||
eventSink,
|
eventSink,
|
||||||
pubsub
|
pubsub,
|
||||||
|
findJobOwner
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,7 +48,12 @@ case class JobStoreModuleBuilder[F[_]: Async](
|
|||||||
object JobStoreModuleBuilder {
|
object JobStoreModuleBuilder {
|
||||||
|
|
||||||
def apply[F[_]: Async](store: Store[F]): JobStoreModuleBuilder[F] =
|
def apply[F[_]: Async](store: Store[F]): JobStoreModuleBuilder[F] =
|
||||||
JobStoreModuleBuilder(store, PubSubT.noop[F], EventSink.silent[F])
|
JobStoreModuleBuilder(
|
||||||
|
store,
|
||||||
|
PubSubT.noop[F],
|
||||||
|
EventSink.silent[F],
|
||||||
|
FindJobOwner.none[F]
|
||||||
|
)
|
||||||
|
|
||||||
final class Module[F[_]](
|
final class Module[F[_]](
|
||||||
val userTasks: UserTaskStore[F],
|
val userTasks: UserTaskStore[F],
|
||||||
@ -51,6 +61,7 @@ object JobStoreModuleBuilder {
|
|||||||
val jobs: JobStore[F],
|
val jobs: JobStore[F],
|
||||||
val store: Store[F],
|
val store: Store[F],
|
||||||
val eventSink: EventSink[F],
|
val eventSink: EventSink[F],
|
||||||
val pubSubT: PubSubT[F]
|
val pubSubT: PubSubT[F],
|
||||||
|
val findJobOwner: FindJobOwner[F]
|
||||||
) extends JobStoreModule[F] {}
|
) extends JobStoreModule[F] {}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
package docspell.scheduler.impl
|
package docspell.scheduler.impl
|
||||||
|
|
||||||
|
import cats.data.OptionT
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
import docspell.common.{Ident, JobState}
|
import docspell.common.{Ident, JobState}
|
||||||
import docspell.notification.api.{Event, EventSink}
|
import docspell.notification.api.{Event, EventSink}
|
||||||
import docspell.pubsub.api.PubSubT
|
import docspell.pubsub.api.PubSubT
|
||||||
@ -19,14 +19,18 @@ import docspell.store.Store
|
|||||||
final class JobStorePublish[F[_]: Sync](
|
final class JobStorePublish[F[_]: Sync](
|
||||||
delegate: JobStore[F],
|
delegate: JobStore[F],
|
||||||
pubsub: PubSubT[F],
|
pubsub: PubSubT[F],
|
||||||
eventSink: EventSink[F]
|
eventSink: EventSink[F],
|
||||||
|
findJobOwner: FindJobOwner[F]
|
||||||
) extends JobStore[F] {
|
) extends JobStore[F] {
|
||||||
|
|
||||||
private def msg(job: Job[String]): JobSubmitted =
|
private def msg(job: Job[String]): JobSubmitted =
|
||||||
JobSubmitted(job.id, job.group, job.task, job.args)
|
JobSubmitted(job.id, job.group, job.task, job.args)
|
||||||
|
|
||||||
private def event(job: Job[String]): Event.JobSubmitted =
|
private def event(job: Job[String]): OptionT[F, Event.JobSubmitted] =
|
||||||
|
OptionT(findJobOwner(job))
|
||||||
|
.map(
|
||||||
Event.JobSubmitted(
|
Event.JobSubmitted(
|
||||||
|
_,
|
||||||
job.id,
|
job.id,
|
||||||
job.group,
|
job.group,
|
||||||
job.task,
|
job.task,
|
||||||
@ -35,10 +39,11 @@ final class JobStorePublish[F[_]: Sync](
|
|||||||
job.subject,
|
job.subject,
|
||||||
job.submitter
|
job.submitter
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
private def publish(job: Job[String]): F[Unit] =
|
private def publish(job: Job[String]): F[Unit] =
|
||||||
pubsub.publish1(JobSubmitted.topic, msg(job)).as(()) *>
|
pubsub.publish1(JobSubmitted.topic, msg(job)).as(()) *>
|
||||||
eventSink.offer(event(job))
|
event(job).semiflatMap(eventSink.offer).value.void
|
||||||
|
|
||||||
private def notifyJoex: F[Unit] =
|
private def notifyJoex: F[Unit] =
|
||||||
pubsub.publish1IgnoreErrors(JobsNotify(), ()).void
|
pubsub.publish1IgnoreErrors(JobsNotify(), ()).void
|
||||||
@ -82,7 +87,8 @@ object JobStorePublish {
|
|||||||
def apply[F[_]: Async](
|
def apply[F[_]: Async](
|
||||||
store: Store[F],
|
store: Store[F],
|
||||||
pubSub: PubSubT[F],
|
pubSub: PubSubT[F],
|
||||||
eventSink: EventSink[F]
|
eventSink: EventSink[F],
|
||||||
|
findJobOwner: FindJobOwner[F]
|
||||||
): JobStore[F] =
|
): JobStore[F] =
|
||||||
new JobStorePublish[F](JobStoreImpl(store), pubSub, eventSink)
|
new JobStorePublish[F](JobStoreImpl(store), pubSub, eventSink, findJobOwner)
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ case class SchedulerBuilder[F[_]: Async](
|
|||||||
queue: JobQueue[F],
|
queue: JobQueue[F],
|
||||||
logSink: LogSink[F],
|
logSink: LogSink[F],
|
||||||
pubSub: PubSubT[F],
|
pubSub: PubSubT[F],
|
||||||
eventSink: EventSink[F]
|
eventSink: EventSink[F],
|
||||||
|
findJobOwner: FindJobOwner[F]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def withConfig(cfg: SchedulerConfig): SchedulerBuilder[F] =
|
def withConfig(cfg: SchedulerConfig): SchedulerBuilder[F] =
|
||||||
@ -32,7 +33,7 @@ case class SchedulerBuilder[F[_]: Async](
|
|||||||
def withTaskRegistry(reg: JobTaskRegistry[F]): SchedulerBuilder[F] =
|
def withTaskRegistry(reg: JobTaskRegistry[F]): SchedulerBuilder[F] =
|
||||||
copy(tasks = reg)
|
copy(tasks = reg)
|
||||||
|
|
||||||
def withTask[A](task: JobTask[F]): SchedulerBuilder[F] =
|
def withTask(task: JobTask[F]): SchedulerBuilder[F] =
|
||||||
withTaskRegistry(tasks.withTask(task))
|
withTaskRegistry(tasks.withTask(task))
|
||||||
|
|
||||||
def withLogSink(sink: LogSink[F]): SchedulerBuilder[F] =
|
def withLogSink(sink: LogSink[F]): SchedulerBuilder[F] =
|
||||||
@ -47,6 +48,9 @@ case class SchedulerBuilder[F[_]: Async](
|
|||||||
def withEventSink(sink: EventSink[F]): SchedulerBuilder[F] =
|
def withEventSink(sink: EventSink[F]): SchedulerBuilder[F] =
|
||||||
copy(eventSink = sink)
|
copy(eventSink = sink)
|
||||||
|
|
||||||
|
def withFindJobOwner(f: FindJobOwner[F]): SchedulerBuilder[F] =
|
||||||
|
copy(findJobOwner = f)
|
||||||
|
|
||||||
def serve: Resource[F, Scheduler[F]] =
|
def serve: Resource[F, Scheduler[F]] =
|
||||||
resource.evalMap(sch => Async[F].start(sch.start.compile.drain).map(_ => sch))
|
resource.evalMap(sch => Async[F].start(sch.start.compile.drain).map(_ => sch))
|
||||||
|
|
||||||
@ -60,6 +64,7 @@ case class SchedulerBuilder[F[_]: Async](
|
|||||||
queue,
|
queue,
|
||||||
pubSub,
|
pubSub,
|
||||||
eventSink,
|
eventSink,
|
||||||
|
findJobOwner,
|
||||||
tasks,
|
tasks,
|
||||||
store,
|
store,
|
||||||
logSink,
|
logSink,
|
||||||
@ -86,6 +91,7 @@ object SchedulerBuilder {
|
|||||||
JobQueue(store),
|
JobQueue(store),
|
||||||
LogSink.db[F](store),
|
LogSink.db[F](store),
|
||||||
PubSubT.noop[F],
|
PubSubT.noop[F],
|
||||||
EventSink.silent[F]
|
EventSink.silent[F],
|
||||||
|
FindJobOwner.none[F]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ final class SchedulerImpl[F[_]: Async](
|
|||||||
queue: JobQueue[F],
|
queue: JobQueue[F],
|
||||||
pubSub: PubSubT[F],
|
pubSub: PubSubT[F],
|
||||||
eventSink: EventSink[F],
|
eventSink: EventSink[F],
|
||||||
|
findJobOwner: FindJobOwner[F],
|
||||||
tasks: JobTaskRegistry[F],
|
tasks: JobTaskRegistry[F],
|
||||||
store: Store[F],
|
store: Store[F],
|
||||||
logSink: LogSink[F],
|
logSink: LogSink[F],
|
||||||
@ -68,8 +69,9 @@ final class SchedulerImpl[F[_]: Async](
|
|||||||
def getRunning: F[Vector[Job[String]]] =
|
def getRunning: F[Vector[Job[String]]] =
|
||||||
state.get
|
state.get
|
||||||
.flatMap(s => QJob.findAll(s.getRunning, store))
|
.flatMap(s => QJob.findAll(s.getRunning, store))
|
||||||
.map(
|
.map(_.map(convertJob))
|
||||||
_.map(rj =>
|
|
||||||
|
private def convertJob(rj: RJob): Job[String] =
|
||||||
Job(
|
Job(
|
||||||
rj.id,
|
rj.id,
|
||||||
rj.task,
|
rj.task,
|
||||||
@ -80,8 +82,6 @@ final class SchedulerImpl[F[_]: Async](
|
|||||||
rj.priority,
|
rj.priority,
|
||||||
rj.tracker
|
rj.tracker
|
||||||
)
|
)
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def requestCancel(jobId: Ident): F[Boolean] =
|
def requestCancel(jobId: Ident): F[Boolean] =
|
||||||
logger.info(s"Scheduler requested to cancel job: ${jobId.id}") *>
|
logger.info(s"Scheduler requested to cancel job: ${jobId.id}") *>
|
||||||
@ -235,8 +235,17 @@ final class SchedulerImpl[F[_]: Async](
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
_ <- Sync[F].whenA(JobState.isDone(finishState))(
|
_ <- Sync[F].whenA(JobState.isDone(finishState))(
|
||||||
eventSink.offer(
|
makeJobDoneEvent(job, result)
|
||||||
Event.JobDone(
|
.semiflatMap(eventSink.offer)
|
||||||
|
.value
|
||||||
|
)
|
||||||
|
} yield ()
|
||||||
|
|
||||||
|
private def makeJobDoneEvent(job: RJob, result: JobTaskResult) =
|
||||||
|
for {
|
||||||
|
acc <- OptionT(findJobOwner(convertJob(job)))
|
||||||
|
ev = Event.JobDone(
|
||||||
|
acc,
|
||||||
job.id,
|
job.id,
|
||||||
job.group,
|
job.group,
|
||||||
job.task,
|
job.task,
|
||||||
@ -247,9 +256,7 @@ final class SchedulerImpl[F[_]: Async](
|
|||||||
result.json.getOrElse(Json.Null),
|
result.json.getOrElse(Json.Null),
|
||||||
result.message
|
result.message
|
||||||
)
|
)
|
||||||
)
|
} yield ev
|
||||||
)
|
|
||||||
} yield ()
|
|
||||||
|
|
||||||
def onStart(job: RJob): F[Unit] =
|
def onStart(job: RJob): F[Unit] =
|
||||||
QJob.setRunning(
|
QJob.setRunning(
|
||||||
|
Reference in New Issue
Block a user