mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-04-04 10:29:34 +00:00
Change routes for scan-mailbox task to allow multiple tasks per user
This commit is contained in:
parent
743aa9d754
commit
9f9dd6c0fb
@ -2,8 +2,10 @@ package docspell.backend.ops
|
||||
|
||||
import cats.implicits._
|
||||
import cats.effect._
|
||||
import cats.data.OptionT
|
||||
import com.github.eikek.calev.CalEvent
|
||||
import io.circe.Encoder
|
||||
import fs2.Stream
|
||||
|
||||
import docspell.store.queue.JobQueue
|
||||
import docspell.store.usertask._
|
||||
@ -11,10 +13,15 @@ import docspell.common._
|
||||
|
||||
trait OUserTask[F[_]] {
|
||||
|
||||
/** Return the settings for the scan-mailbox task of the current user.
|
||||
* There is at most one such task per user.
|
||||
/** Return the settings for all scan-mailbox tasks of the current user.
|
||||
*/
|
||||
def getScanMailbox(account: AccountId): F[UserTask[ScanMailboxArgs]]
|
||||
def getScanMailbox(account: AccountId): Stream[F, UserTask[ScanMailboxArgs]]
|
||||
|
||||
/** Find a scan-mailbox task by the given id. */
|
||||
def findScanMailbox(
|
||||
id: Ident,
|
||||
account: AccountId
|
||||
): OptionT[F, UserTask[ScanMailboxArgs]]
|
||||
|
||||
/** Updates the scan-mailbox tasks and notifies the joex nodes.
|
||||
*/
|
||||
@ -24,7 +31,9 @@ trait OUserTask[F[_]] {
|
||||
): F[Unit]
|
||||
|
||||
/** Return the settings for the notify-due-items task of the current
|
||||
* user. There is at most one such task per user.
|
||||
* user. There is at most one such task per user. If no task has
|
||||
* been created/submitted a new one with default values is
|
||||
* returned.
|
||||
*/
|
||||
def getNotifyDueItems(account: AccountId): F[UserTask[NotifyDueItemsArgs]]
|
||||
|
||||
@ -35,6 +44,9 @@ trait OUserTask[F[_]] {
|
||||
task: UserTask[NotifyDueItemsArgs]
|
||||
): F[Unit]
|
||||
|
||||
/** Removes a user task with the given id. */
|
||||
def deleteTask(account: AccountId, id: Ident): F[Unit]
|
||||
|
||||
/** Discards the schedule and immediately submits the task to the job
|
||||
* executor's queue. It will not update the corresponding periodic
|
||||
* task.
|
||||
@ -63,17 +75,28 @@ object OUserTask {
|
||||
_ <- joex.notifyAllNodes
|
||||
} yield ()
|
||||
|
||||
def getScanMailbox(account: AccountId): F[UserTask[ScanMailboxArgs]] =
|
||||
def getScanMailbox(account: AccountId): Stream[F, UserTask[ScanMailboxArgs]] =
|
||||
store
|
||||
.getOneByName[ScanMailboxArgs](account, ScanMailboxArgs.taskName)
|
||||
.getOrElseF(scanMailboxDefault(account))
|
||||
.getByName[ScanMailboxArgs](account, ScanMailboxArgs.taskName)
|
||||
|
||||
def findScanMailbox(
|
||||
id: Ident,
|
||||
account: AccountId
|
||||
): OptionT[F, UserTask[ScanMailboxArgs]] =
|
||||
OptionT(getScanMailbox(account).find(_.id == id).compile.last)
|
||||
|
||||
def deleteTask(account: AccountId, id: Ident): F[Unit] =
|
||||
(for {
|
||||
_ <- store.getByIdRaw(account, id)
|
||||
_ <- OptionT.liftF(store.deleteTask(account, id))
|
||||
} yield ()).getOrElse(())
|
||||
|
||||
def submitScanMailbox(
|
||||
account: AccountId,
|
||||
task: UserTask[ScanMailboxArgs]
|
||||
): F[Unit] =
|
||||
for {
|
||||
_ <- store.updateOneTask[ScanMailboxArgs](account, task)
|
||||
_ <- store.updateTask[ScanMailboxArgs](account, task)
|
||||
_ <- joex.notifyAllNodes
|
||||
} yield ()
|
||||
|
||||
@ -113,26 +136,26 @@ object OUserTask {
|
||||
)
|
||||
)
|
||||
|
||||
private def scanMailboxDefault(
|
||||
account: AccountId
|
||||
): F[UserTask[ScanMailboxArgs]] =
|
||||
for {
|
||||
id <- Ident.randomId[F]
|
||||
} yield UserTask(
|
||||
id,
|
||||
ScanMailboxArgs.taskName,
|
||||
false,
|
||||
CalEvent.unsafe("*-*-* 0,12:00"),
|
||||
ScanMailboxArgs(
|
||||
account,
|
||||
Ident.unsafe(""),
|
||||
Nil,
|
||||
Some(Duration.hours(12)),
|
||||
None,
|
||||
false,
|
||||
None
|
||||
)
|
||||
)
|
||||
// private def scanMailboxDefault(
|
||||
// account: AccountId
|
||||
// ): F[UserTask[ScanMailboxArgs]] =
|
||||
// for {
|
||||
// id <- Ident.randomId[F]
|
||||
// } yield UserTask(
|
||||
// id,
|
||||
// ScanMailboxArgs.taskName,
|
||||
// false,
|
||||
// CalEvent.unsafe("*-*-* 0,12:00"),
|
||||
// ScanMailboxArgs(
|
||||
// account,
|
||||
// Ident.unsafe(""),
|
||||
// Nil,
|
||||
// Some(Duration.hours(12)),
|
||||
// None,
|
||||
// false,
|
||||
// None
|
||||
// )
|
||||
// )
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,13 @@ import cats.effect.Sync
|
||||
import io.circe.{Decoder, Encoder}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
case class Ident(id: String) {}
|
||||
case class Ident(id: String) {
|
||||
def isEmpty: Boolean =
|
||||
id.trim.isEmpty
|
||||
|
||||
def nonEmpty: Boolean =
|
||||
!isEmpty
|
||||
}
|
||||
|
||||
object Ident {
|
||||
implicit val identEq: Eq[Ident] =
|
||||
|
@ -1760,9 +1760,10 @@ paths:
|
||||
tags: [ User Tasks ]
|
||||
summary: Get settings for "Scan Mailbox" task
|
||||
description: |
|
||||
Return the current settings for the scan mailbox task of the
|
||||
Return the current settings for the scan-mailbox tasks of the
|
||||
authenticated user. Users can periodically fetch mails to be
|
||||
imported into docspell.
|
||||
imported into docspell. It is possible to have multiple of
|
||||
these tasks.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
responses:
|
||||
@ -1771,13 +1772,13 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ScanMailboxSettings"
|
||||
$ref: "#/components/schemas/ScanMailboxSettingsList"
|
||||
post:
|
||||
tags: [ User Tasks ]
|
||||
summary: Change current settings for "Scan Mailbox" task
|
||||
summary: Create settings for "Scan Mailbox" task
|
||||
description: |
|
||||
Change the current settings for the scan-mailbox task of the
|
||||
authenticated user.
|
||||
Create new settings for a scan-mailbox task. The id field in
|
||||
the input data is ignored.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
requestBody:
|
||||
@ -1792,6 +1793,61 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
put:
|
||||
tags: [ User Tasks ]
|
||||
summary: Change current settings for "Scan Mailbox" task
|
||||
description: |
|
||||
Change the settings for a scan-mailbox task. The task is
|
||||
looked up by its id.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ScanMailboxSettings"
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
/sec/usertask/scanmailbox/{id}:
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/id"
|
||||
get:
|
||||
tags: [ User Tasks ]
|
||||
summary: Get settings for "Scan Mailbox" task
|
||||
description: |
|
||||
Return the current settings for a single scan-mailbox task of
|
||||
the authenticated user. Users can periodically fetch mails to
|
||||
be imported into docspell.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ScanMailboxSettings"
|
||||
delete:
|
||||
tags: [ User Tasks ]
|
||||
summary: Delete a scan-mailbox task.
|
||||
description: |
|
||||
Deletes the settings to a scan-mailbox task of the
|
||||
authenticated user.
|
||||
security:
|
||||
- authTokenHeader: []
|
||||
responses:
|
||||
200:
|
||||
description: Ok
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/BasicResult"
|
||||
|
||||
/sec/usertask/scanmailbox/startonce:
|
||||
post:
|
||||
tags: [ User Tasks ]
|
||||
@ -1816,6 +1872,16 @@ paths:
|
||||
|
||||
components:
|
||||
schemas:
|
||||
ScanMailboxSettingsList:
|
||||
description: |
|
||||
A list of scan-mailbox tasks.
|
||||
required:
|
||||
- items
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ScanMailboxSettings"
|
||||
ScanMailboxSettings:
|
||||
description: |
|
||||
Settings for the scan mailbox task.
|
||||
|
@ -2,6 +2,7 @@ package docspell.restserver.routes
|
||||
|
||||
import cats.effect._
|
||||
import cats.implicits._
|
||||
import cats.data.OptionT
|
||||
import org.http4s._
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
import org.http4s.circe.CirceEntityEncoder._
|
||||
@ -25,10 +26,18 @@ object ScanMailboxRoutes {
|
||||
import dsl._
|
||||
|
||||
HttpRoutes.of {
|
||||
case GET -> Root / Ident(id) =>
|
||||
(for {
|
||||
task <- ut.findScanMailbox(id, user.account)
|
||||
res <- OptionT.liftF(taskToSettings(user.account, backend, task))
|
||||
resp <- OptionT.liftF(Ok(res))
|
||||
} yield resp).getOrElseF(NotFound())
|
||||
|
||||
case req @ POST -> Root / "startonce" =>
|
||||
for {
|
||||
data <- req.as[ScanMailboxSettings]
|
||||
task = makeTask(user.account, data)
|
||||
data <- req.as[ScanMailboxSettings]
|
||||
newId <- Ident.randomId[F]
|
||||
task <- makeTask(newId, user.account, data)
|
||||
res <-
|
||||
ut.executeNow(user.account, task)
|
||||
.attempt
|
||||
@ -36,43 +45,74 @@ object ScanMailboxRoutes {
|
||||
resp <- Ok(res)
|
||||
} yield resp
|
||||
|
||||
case GET -> Root =>
|
||||
case DELETE -> Root / Ident(id) =>
|
||||
for {
|
||||
task <- ut.getScanMailbox(user.account)
|
||||
res <- taskToSettings(user.account, backend, task)
|
||||
res <-
|
||||
ut.deleteTask(user.account, id)
|
||||
.attempt
|
||||
.map(Conversions.basicResult(_, "Deleted successfully."))
|
||||
resp <- Ok(res)
|
||||
} yield resp
|
||||
|
||||
case req @ PUT -> Root =>
|
||||
def run(data: ScanMailboxSettings) =
|
||||
for {
|
||||
task <- makeTask(data.id, user.account, data)
|
||||
res <-
|
||||
ut.submitScanMailbox(user.account, task)
|
||||
.attempt
|
||||
.map(Conversions.basicResult(_, "Saved successfully."))
|
||||
resp <- Ok(res)
|
||||
} yield resp
|
||||
for {
|
||||
data <- req.as[ScanMailboxSettings]
|
||||
resp <-
|
||||
if (data.id.isEmpty) Ok(BasicResult(false, "Empty id is not allowed"))
|
||||
else run(data)
|
||||
} yield resp
|
||||
|
||||
case req @ POST -> Root =>
|
||||
for {
|
||||
data <- req.as[ScanMailboxSettings]
|
||||
task = makeTask(user.account, data)
|
||||
data <- req.as[ScanMailboxSettings]
|
||||
newId <- Ident.randomId[F]
|
||||
task <- makeTask(newId, user.account, data)
|
||||
res <-
|
||||
ut.submitScanMailbox(user.account, task)
|
||||
.attempt
|
||||
.map(Conversions.basicResult(_, "Saved successfully."))
|
||||
resp <- Ok(res)
|
||||
} yield resp
|
||||
|
||||
case GET -> Root =>
|
||||
ut.getScanMailbox(user.account)
|
||||
.evalMap(task => taskToSettings(user.account, backend, task))
|
||||
.compile
|
||||
.toVector
|
||||
.map(v => ScanMailboxSettingsList(v.toList))
|
||||
.flatMap(Ok(_))
|
||||
}
|
||||
}
|
||||
|
||||
def makeTask(
|
||||
def makeTask[F[_]: Sync](
|
||||
id: Ident,
|
||||
user: AccountId,
|
||||
settings: ScanMailboxSettings
|
||||
): UserTask[ScanMailboxArgs] =
|
||||
UserTask(
|
||||
settings.id,
|
||||
ScanMailboxArgs.taskName,
|
||||
settings.enabled,
|
||||
settings.schedule,
|
||||
ScanMailboxArgs(
|
||||
user,
|
||||
settings.imapConnection,
|
||||
settings.folders,
|
||||
settings.receivedSinceHours.map(_.toLong).map(Duration.hours),
|
||||
settings.targetFolder,
|
||||
settings.deleteMail,
|
||||
settings.direction
|
||||
): F[UserTask[ScanMailboxArgs]] =
|
||||
Sync[F].pure(
|
||||
UserTask(
|
||||
id,
|
||||
ScanMailboxArgs.taskName,
|
||||
settings.enabled,
|
||||
settings.schedule,
|
||||
ScanMailboxArgs(
|
||||
user,
|
||||
settings.imapConnection,
|
||||
settings.folders,
|
||||
settings.receivedSinceHours.map(_.toLong).map(Duration.hours),
|
||||
settings.targetFolder,
|
||||
settings.deleteMail,
|
||||
settings.direction
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -31,6 +31,20 @@ object QUserTask {
|
||||
)
|
||||
).query[RPeriodicTask].stream.map(makeUserTask)
|
||||
|
||||
def findById(
|
||||
account: AccountId,
|
||||
id: Ident
|
||||
): ConnectionIO[Option[UserTask[String]]] =
|
||||
selectSimple(
|
||||
RPeriodicTask.Columns.all,
|
||||
RPeriodicTask.table,
|
||||
and(
|
||||
cols.group.is(account.collective),
|
||||
cols.submitter.is(account.user),
|
||||
cols.id.is(id)
|
||||
)
|
||||
).query[RPeriodicTask].option.map(_.map(makeUserTask))
|
||||
|
||||
def insert(account: AccountId, task: UserTask[String]): ConnectionIO[Int] =
|
||||
for {
|
||||
r <- task.toPeriodicTask[ConnectionIO](account)
|
||||
|
@ -42,12 +42,14 @@ trait UserTaskStore[F[_]] {
|
||||
D: Decoder[A]
|
||||
): Stream[F, UserTask[A]]
|
||||
|
||||
/** Return a user-task with the given id. */
|
||||
def getByIdRaw(account: AccountId, id: Ident): OptionT[F, UserTask[String]]
|
||||
|
||||
/** Updates or inserts the given task.
|
||||
*
|
||||
* The task is identified by its id. If no task with this id
|
||||
* exists, a new one is created. Otherwise the existing task is
|
||||
* updated. The job executors are notified if a task has been
|
||||
* enabled.
|
||||
* updated.
|
||||
*/
|
||||
def updateTask[A](account: AccountId, ut: UserTask[A])(implicit E: Encoder[A]): F[Int]
|
||||
|
||||
@ -100,6 +102,9 @@ object UserTaskStore {
|
||||
def getByNameRaw(account: AccountId, name: Ident): Stream[F, UserTask[String]] =
|
||||
store.transact(QUserTask.findByName(account, name))
|
||||
|
||||
def getByIdRaw(account: AccountId, id: Ident): OptionT[F, UserTask[String]] =
|
||||
OptionT(store.transact(QUserTask.findById(account, id)))
|
||||
|
||||
def getByName[A](account: AccountId, name: Ident)(implicit
|
||||
D: Decoder[A]
|
||||
): Stream[F, UserTask[A]] =
|
||||
|
Loading…
x
Reference in New Issue
Block a user