From eabcffe71a57c6b763b6db6deddbe9e2273db8a8 Mon Sep 17 00:00:00 2001 From: eikek Date: Thu, 4 Aug 2022 12:44:09 +0200 Subject: [PATCH] Adopt restserver to new collective-id --- .../docspell/backend/ops/OCollective.scala | 2 - .../docspell/backend/ops/ODownloadAll.scala | 10 +-- .../scala/docspell/backend/ops/OFolder.scala | 14 ++-- .../scala/docspell/backend/ops/OJob.scala | 42 +++++++--- .../docspell/common/ScanMailboxArgs.scala | 2 +- .../api/PeriodicDueItemsArgs.scala | 2 +- .../notification/api/PeriodicQueryArgs.scala | 2 +- .../docspell/restserver/auth/CookieData.scala | 6 +- .../restserver/conv/Conversions.scala | 34 +++++--- .../routes/AddonArchiveRoutes.scala | 8 +- .../routes/AddonRunConfigRoutes.scala | 8 +- .../restserver/routes/AddonRunRoutes.scala | 8 +- .../routes/AttachmentMultiRoutes.scala | 2 +- .../restserver/routes/AttachmentRoutes.scala | 36 ++++---- .../restserver/routes/CheckFileRoutes.scala | 2 +- .../routes/ClientSettingsRoutes.scala | 30 +++++-- .../restserver/routes/CollectiveRoutes.scala | 19 ++--- .../restserver/routes/CustomFieldRoutes.scala | 10 +-- .../restserver/routes/DownloadAllRoutes.scala | 8 +- .../restserver/routes/EquipmentRoutes.scala | 10 +-- .../restserver/routes/FolderRoutes.scala | 28 ++++--- .../routes/FullTextIndexRoutes.scala | 4 +- .../routes/IntegrationEndpointRoutes.scala | 30 +++---- .../restserver/routes/ItemLinkRoutes.scala | 10 +-- .../restserver/routes/ItemMultiRoutes.scala | 51 +++++++----- .../restserver/routes/ItemRoutes.scala | 82 +++++++++++-------- .../restserver/routes/JobQueueRoutes.scala | 9 +- .../restserver/routes/LoginRoutes.scala | 2 +- .../restserver/routes/MailSendRoutes.scala | 4 +- .../routes/MailSettingsRoutes.scala | 24 +++--- .../routes/NotificationRoutes.scala | 18 ++-- .../routes/NotifyDueItemsRoutes.scala | 2 +- .../routes/OrganizationRoutes.scala | 17 ++-- .../routes/PeriodicQueryRoutes.scala | 2 +- .../restserver/routes/PersonRoutes.scala | 13 ++- .../restserver/routes/ScanMailboxRoutes.scala | 10 +-- .../restserver/routes/SentMailRoutes.scala | 6 +- .../restserver/routes/ShareRoutes.scala | 17 ++-- .../restserver/routes/SourceRoutes.scala | 8 +- .../restserver/routes/TagRoutes.scala | 8 +- .../restserver/routes/UploadRoutes.scala | 22 +++-- .../restserver/routes/UserRoutes.scala | 36 +++++--- .../restserver/ws/WebSocketRoutes.scala | 5 +- .../scala/db/migration/MigrationTasks.scala | 20 +++-- .../migration/data/PeriodicDueItemsArgs.scala | 43 ++++++++++ .../db/migration/data/PeriodicQueryArgs.scala | 33 ++++++++ .../db/migration/data/ScanMailboxArgs.scala | 69 ++++++++++++++++ .../docspell/store/records/RFolder.scala | 4 +- 48 files changed, 539 insertions(+), 293 deletions(-) create mode 100644 modules/store/src/main/scala/db/migration/data/PeriodicDueItemsArgs.scala create mode 100644 modules/store/src/main/scala/db/migration/data/PeriodicQueryArgs.scala create mode 100644 modules/store/src/main/scala/db/migration/data/ScanMailboxArgs.scala diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OCollective.scala b/modules/backend/src/main/scala/docspell/backend/ops/OCollective.scala index 0613cd06..41b3bfdd 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OCollective.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OCollective.scala @@ -28,7 +28,6 @@ trait OCollective[F[_]] { def updateSettings( collective: CollectiveId, - collectiveName: Ident, settings: OCollective.Settings ): F[AddResult] @@ -147,7 +146,6 @@ object OCollective { def updateSettings( collectiveId: CollectiveId, - collectiveName: Ident, sett: Settings ): F[AddResult] = store diff --git a/modules/backend/src/main/scala/docspell/backend/ops/ODownloadAll.scala b/modules/backend/src/main/scala/docspell/backend/ops/ODownloadAll.scala index 6cec4b11..99d4a9c0 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/ODownloadAll.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/ODownloadAll.scala @@ -40,10 +40,10 @@ trait ODownloadAll[F[_]] { def submit(accountId: AccountInfo, req: DownloadRequest): F[DownloadSummary] /** Given the id from the summary, cancels a running job. */ - def cancelDownload(accountId: AccountId, id: Ident): F[OJob.JobCancelResult] + def cancelDownload(cid: CollectiveId, id: Ident): F[OJob.JobCancelResult] /** Returns the file if it is present, given a summary id. */ - def getFile(collective: Ident, id: Ident): F[Option[DownloadAllFile[F]]] + def getFile(collective: CollectiveId, id: Ident): F[Option[DownloadAllFile[F]]] /** Deletes a download archive given it's id. */ def deleteFile(id: Ident): F[Unit] @@ -122,7 +122,7 @@ object ODownloadAll { else DownloadState.NotPresent } yield state - def getFile(collective: Ident, id: Ident) = + def getFile(collective: CollectiveId, id: Ident) = OptionT(store.transact(RDownloadQuery.findById(id))) .map(_._2) .map(md => @@ -156,10 +156,10 @@ object ODownloadAll { _ <- store.fileRepo.delete(fkey) } yield () - def cancelDownload(accountId: AccountId, id: Ident) = + def cancelDownload(cid: CollectiveId, id: Ident) = OptionT(store.transact(RDownloadQuery.findById(id))) .flatMap(t => OptionT(store.transact(RJob.findNonFinalByTracker(t._1.id)))) - .semiflatMap(job => jobs.cancelJob(job.id, accountId.collective)) + .semiflatMap(job => jobs.cancelJob(job.id, UserTaskScope.collective(cid))) .getOrElse(JobCancelResult.jobNotFound) } diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OFolder.scala b/modules/backend/src/main/scala/docspell/backend/ops/OFolder.scala index ea8a95a5..744d3d02 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OFolder.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OFolder.scala @@ -30,10 +30,10 @@ trait OFolder[F[_]] { userId: Ident ): F[Option[OFolder.FolderDetail]] - /** Adds a new folder. If `login` is non-empty, the `folder.user` property is ignored - * and the user-id is determined by the given login name. + /** Adds a new folder. If `login` is non-empty, the `folder.owner` property is ignored + * and its value is determined by the given login name. */ - def add(folder: RFolder, userId: Option[Ident]): F[AddResult] + def add(folder: RFolder, login: Option[Ident]): F[AddResult] def changeName( folder: Ident, @@ -123,11 +123,11 @@ object OFolder { ): F[Option[FolderDetail]] = store.transact(QFolder.findById(id, collectiveId, userId)) - def add(folder: RFolder, userId: Option[Ident]): F[AddResult] = { - val insert = userId match { - case Some(uid) => + def add(folder: RFolder, login: Option[Ident]): F[AddResult] = { + val insert = login match { + case Some(userLogin) => for { - user <- RUser.findById(uid, folder.collectiveId.some) + user <- RUser.findByLogin(userLogin, folder.collectiveId.some) s = user.map(u => folder.copy(owner = u.uid)).getOrElse(folder) n <- RFolder.insert(s) } yield n diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala b/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala index ba848c7f..67923b7d 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OJob.scala @@ -9,11 +9,11 @@ package docspell.backend.ops import cats.data.OptionT import cats.effect._ import cats.implicits._ - import docspell.backend.ops.OJob.{CollectiveQueueState, JobCancelResult} import docspell.common._ import docspell.pubsub.api.PubSubT import docspell.scheduler.msg.JobDone +import docspell.scheduler.usertask.UserTaskScope import docspell.store.Store import docspell.store.UpdateResult import docspell.store.queries.QJobQueue @@ -21,13 +21,13 @@ import docspell.store.records.{RJob, RJobLog} trait OJob[F[_]] { - def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState] + def queueState(collective: UserTaskScope, maxResults: Int): F[CollectiveQueueState] - def cancelJob(id: Ident, collective: Ident): F[JobCancelResult] + def cancelJob(id: Ident, collective: UserTaskScope): F[JobCancelResult] - def setPriority(id: Ident, collective: Ident, prio: Priority): F[UpdateResult] + def setPriority(id: Ident, collective: UserTaskScope, prio: Priority): F[UpdateResult] - def getUnfinishedJobCount(collective: Ident): F[Int] + def getUnfinishedJobCount(collective: UserTaskScope): F[Int] } object OJob { @@ -61,20 +61,34 @@ object OJob { Resource.pure[F, OJob[F]](new OJob[F] { private[this] val logger = docspell.logging.getLogger[F] - def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState] = + private def scopeToGroup(s: UserTaskScope) = + s.collectiveId + .map(_.valueAsIdent) + .getOrElse(DocspellSystem.taskGroup) + + def queueState( + collective: UserTaskScope, + maxResults: Int + ): F[CollectiveQueueState] = store .transact( - QJobQueue.queueStateSnapshot(collective, maxResults.toLong) + QJobQueue.queueStateSnapshot(scopeToGroup(collective), maxResults.toLong) ) .map(t => JobDetail(t._1, t._2)) .compile .toVector .map(CollectiveQueueState) - def setPriority(id: Ident, collective: Ident, prio: Priority): F[UpdateResult] = - UpdateResult.fromUpdate(store.transact(RJob.setPriority(id, collective, prio))) + def setPriority( + id: Ident, + collective: UserTaskScope, + prio: Priority + ): F[UpdateResult] = + UpdateResult.fromUpdate( + store.transact(RJob.setPriority(id, scopeToGroup(collective), prio)) + ) - def cancelJob(id: Ident, collective: Ident): F[JobCancelResult] = { + def cancelJob(id: Ident, collective: UserTaskScope): F[JobCancelResult] = { def remove(job: RJob): F[JobCancelResult] = for { n <- store.transact(RJob.delete(job.id)) @@ -99,7 +113,9 @@ object OJob { } (for { - job <- OptionT(store.transact(RJob.findByIdAndGroup(id, collective))) + job <- OptionT( + store.transact(RJob.findByIdAndGroup(id, scopeToGroup(collective))) + ) result <- OptionT.liftF( if (job.isInProgress) tryCancel(job) else remove(job) @@ -108,7 +124,7 @@ object OJob { .getOrElse(JobCancelResult.jobNotFound) } - def getUnfinishedJobCount(collective: Ident): F[Int] = - store.transact(RJob.getUnfinishedCount(collective)) + def getUnfinishedJobCount(collective: UserTaskScope): F[Int] = + store.transact(RJob.getUnfinishedCount(scopeToGroup(collective))) }) } diff --git a/modules/common/src/main/scala/docspell/common/ScanMailboxArgs.scala b/modules/common/src/main/scala/docspell/common/ScanMailboxArgs.scala index 9b886c90..963c3808 100644 --- a/modules/common/src/main/scala/docspell/common/ScanMailboxArgs.scala +++ b/modules/common/src/main/scala/docspell/common/ScanMailboxArgs.scala @@ -20,7 +20,7 @@ import io.circe.generic.semiauto._ */ case class ScanMailboxArgs( // the docspell user account - account: AccountId, + account: AccountInfo, // the configured imap connection imapConnection: Ident, // scan folders recursively diff --git a/modules/notification/api/src/main/scala/docspell/notification/api/PeriodicDueItemsArgs.scala b/modules/notification/api/src/main/scala/docspell/notification/api/PeriodicDueItemsArgs.scala index 7de991cf..e71253b3 100644 --- a/modules/notification/api/src/main/scala/docspell/notification/api/PeriodicDueItemsArgs.scala +++ b/modules/notification/api/src/main/scala/docspell/notification/api/PeriodicDueItemsArgs.scala @@ -21,7 +21,7 @@ import io.circe.{Decoder, Encoder} * the json data of the corresponding task. */ final case class PeriodicDueItemsArgs( - account: AccountId, + account: AccountInfo, channels: NonEmptyList[ChannelRef], remindDays: Int, daysBack: Option[Int], diff --git a/modules/notification/api/src/main/scala/docspell/notification/api/PeriodicQueryArgs.scala b/modules/notification/api/src/main/scala/docspell/notification/api/PeriodicQueryArgs.scala index 8e0dd405..01f4497d 100644 --- a/modules/notification/api/src/main/scala/docspell/notification/api/PeriodicQueryArgs.scala +++ b/modules/notification/api/src/main/scala/docspell/notification/api/PeriodicQueryArgs.scala @@ -14,7 +14,7 @@ import io.circe.generic.semiauto import io.circe.{Decoder, Encoder} final case class PeriodicQueryArgs( - account: AccountId, + account: AccountInfo, channels: NonEmptyList[ChannelRef], query: Option[ItemQueryString], bookmark: Option[String], diff --git a/modules/restserver/src/main/scala/docspell/restserver/auth/CookieData.scala b/modules/restserver/src/main/scala/docspell/restserver/auth/CookieData.scala index 531ed9bf..577276f1 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/auth/CookieData.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/auth/CookieData.scala @@ -7,14 +7,12 @@ package docspell.restserver.auth import docspell.backend.auth._ -import docspell.common.AccountId -import docspell.common.LenientUri - +import docspell.common.{AccountInfo, LenientUri} import org.http4s._ import org.typelevel.ci.CIString case class CookieData(auth: AuthToken) { - def accountId: AccountId = auth.account + def accountInfo: AccountInfo = auth.account def asString: String = auth.asString def asCookie(baseUrl: LenientUri): ResponseCookie = { diff --git a/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala b/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala index 806b69c8..38636cc3 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/conv/Conversions.scala @@ -371,7 +371,10 @@ trait Conversions { ) } - def newOrg[F[_]: Sync](v: Organization, cid: Ident): F[OOrganization.OrgAndContacts] = { + def newOrg[F[_]: Sync]( + v: Organization, + cid: CollectiveId + ): F[OOrganization.OrgAndContacts] = { def contacts(oid: Ident) = v.contacts.traverse(c => newContact(c, oid.some, None)) for { @@ -397,7 +400,7 @@ trait Conversions { def changeOrg[F[_]: Sync]( v: Organization, - cid: Ident + cid: CollectiveId ): F[OOrganization.OrgAndContacts] = { def contacts(oid: Ident) = v.contacts.traverse(c => newContact(c, oid.some, None)) @@ -435,7 +438,10 @@ trait Conversions { ) } - def newPerson[F[_]: Sync](v: Person, cid: Ident): F[OOrganization.PersonAndContacts] = { + def newPerson[F[_]: Sync]( + v: Person, + cid: CollectiveId + ): F[OOrganization.PersonAndContacts] = { def contacts(pid: Ident) = v.contacts.traverse(c => newContact(c, None, pid.some)) for { @@ -461,7 +467,7 @@ trait Conversions { def changePerson[F[_]: Sync]( v: Person, - cid: Ident + cid: CollectiveId ): F[OOrganization.PersonAndContacts] = { def contacts(pid: Ident) = v.contacts.traverse(c => newContact(c, None, pid.some)) @@ -512,7 +518,7 @@ trait Conversions { ru.created ) - def newUser[F[_]: Sync](u: User, cid: Ident): F[RUser] = + def newUser[F[_]: Sync](u: User, cid: CollectiveId): F[RUser] = Conversions.timeId.map { case (id, now) => RUser( id, @@ -528,7 +534,7 @@ trait Conversions { ) } - def changeUser(u: User, cid: Ident): RUser = + def changeUser(u: User, cid: CollectiveId): RUser = RUser( u.id, u.login, @@ -547,12 +553,12 @@ trait Conversions { def mkTag(rt: RTag): Tag = Tag(rt.tagId, rt.name, rt.category, rt.created) - def newTag[F[_]: Sync](t: Tag, cid: Ident): F[RTag] = + def newTag[F[_]: Sync](t: Tag, cid: CollectiveId): F[RTag] = Conversions.timeId.map { case (id, now) => RTag(id, cid, t.name.trim, t.category.map(_.trim), now) } - def changeTag(t: Tag, cid: Ident): RTag = + def changeTag(t: Tag, cid: CollectiveId): RTag = RTag(t.id, cid, t.name.trim, t.category.map(_.trim), t.created) // sources @@ -575,7 +581,7 @@ trait Conversions { TagList(s.tags.length, s.tags.map(mkTag).toList) ) - def newSource[F[_]: Sync](s: Source, cid: Ident): F[RSource] = + def newSource[F[_]: Sync](s: Source, cid: CollectiveId): F[RSource] = Conversions.timeId.map { case (id, now) => RSource( id, @@ -593,10 +599,10 @@ trait Conversions { ) } - def changeSource[F[_]](s: Source, coll: Ident): RSource = + def changeSource(s: Source, cid: CollectiveId): RSource = RSource( s.id, - coll, + cid, s.abbrev.trim, s.description, s.counter, @@ -613,12 +619,12 @@ trait Conversions { def mkEquipment(re: REquipment): Equipment = Equipment(re.eid, re.name, re.created, re.notes, re.use) - def newEquipment[F[_]: Sync](e: Equipment, cid: Ident): F[REquipment] = + def newEquipment[F[_]: Sync](e: Equipment, cid: CollectiveId): F[REquipment] = Conversions.timeId.map { case (id, now) => REquipment(id, cid, e.name.trim, now, now, e.notes, e.use) } - def changeEquipment[F[_]: Sync](e: Equipment, cid: Ident): F[REquipment] = + def changeEquipment[F[_]: Sync](e: Equipment, cid: CollectiveId): F[REquipment] = Timestamp .current[F] .map(now => REquipment(e.id, cid, e.name.trim, e.created, now, e.notes, e.use)) @@ -681,6 +687,8 @@ trait Conversions { case UploadResult.NoFiles => BasicResult(false, "There were no files to submit.") case UploadResult.NoSource => BasicResult(false, "The source id is not valid.") case UploadResult.NoItem => BasicResult(false, "The item could not be found.") + case UploadResult.NoCollective => + BasicResult(false, "The collective could not be found.") } def basicResult(cr: PassChangeResult): BasicResult = diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/AddonArchiveRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/AddonArchiveRoutes.scala index 72c9b808..6995ebb8 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/AddonArchiveRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/AddonArchiveRoutes.scala @@ -39,7 +39,7 @@ object AddonArchiveRoutes extends AddonValidationSupport { HttpRoutes.of { case GET -> Root => for { - all <- backend.addons.getAllAddons(token.account.collective) + all <- backend.addons.getAllAddons(token.account.collectiveId) resp <- Ok( AddonList( all.map(r => @@ -62,7 +62,7 @@ object AddonArchiveRoutes extends AddonValidationSupport { for { input <- req.as[AddonRegister] install = backend.addons.registerAddon( - token.account.collective, + token.account.collectiveId, input.url, None ) @@ -82,7 +82,7 @@ object AddonArchiveRoutes extends AddonValidationSupport { s"Addon updated: ${m.nameAndVersion}" ) ) - val update = backend.addons.refreshAddon(token.account.collective, id) + val update = backend.addons.refreshAddon(token.account.collectiveId, id) for { resp <- if (sync) @@ -97,7 +97,7 @@ object AddonArchiveRoutes extends AddonValidationSupport { case DELETE -> Root / Ident(id) => for { - flag <- backend.addons.deleteAddon(token.account.collective, id) + flag <- backend.addons.deleteAddon(token.account.collectiveId, id) resp <- if (flag) Ok(BasicResult(true, "Addon deleted")) else NotFound(BasicResult(false, "Addon not found")) diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/AddonRunConfigRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/AddonRunConfigRoutes.scala index d146eef1..4f93c9cf 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/AddonRunConfigRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/AddonRunConfigRoutes.scala @@ -29,7 +29,7 @@ object AddonRunConfigRoutes { HttpRoutes.of { case GET -> Root => for { - all <- backend.addons.getAllAddonRunConfigs(token.account.collective) + all <- backend.addons.getAllAddonRunConfigs(token.account.collectiveId) resp <- Ok(AddonRunConfigList(all.map(convertInfoTask))) } yield resp @@ -39,7 +39,7 @@ object AddonRunConfigRoutes { data = convertInsertTask(Ident.unsafe(""), input) res <- data.flatTraverse(in => backend.addons - .upsertAddonRunConfig(token.account.collective, in) + .upsertAddonRunConfig(token.account.collectiveId, in) .map(_.leftMap(_.message)) ) resp <- res.fold( @@ -54,7 +54,7 @@ object AddonRunConfigRoutes { data = convertInsertTask(id, input) res <- data.flatTraverse(in => backend.addons - .upsertAddonRunConfig(token.account.collective, in) + .upsertAddonRunConfig(token.account.collectiveId, in) .map(_.leftMap(_.message)) ) resp <- res.fold( @@ -65,7 +65,7 @@ object AddonRunConfigRoutes { case DELETE -> Root / Ident(id) => for { - flag <- backend.addons.deleteAddonRunConfig(token.account.collective, id) + flag <- backend.addons.deleteAddonRunConfig(token.account.collectiveId, id) resp <- if (flag) Ok(BasicResult(true, "Addon task deleted")) else NotFound(BasicResult(false, "Addon task not found")) diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/AddonRunRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/AddonRunRoutes.scala index 3b167b2d..25eb46fc 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/AddonRunRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/AddonRunRoutes.scala @@ -9,12 +9,11 @@ package docspell.restserver.routes import cats.data.NonEmptyList import cats.effect._ import cats.syntax.all._ - import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.restapi.model._ import docspell.restserver.http4s.ThrowableResponseMapper - +import docspell.scheduler.usertask.UserTaskScope import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityCodec._ import org.http4s.dsl.Http4sDsl @@ -29,9 +28,10 @@ object AddonRunRoutes { for { input <- req.as[AddonRunExistingItem] _ <- backend.addons.runAddonForItem( - token.account, + token.account.collectiveId, NonEmptyList(input.itemId, input.additionalItems), - input.addonRunConfigIds.toSet + input.addonRunConfigIds.toSet, + UserTaskScope(token.account) ) resp <- Ok(BasicResult(true, "Job for running addons submitted.")) } yield resp diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/AttachmentMultiRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/AttachmentMultiRoutes.scala index 6a0baa9e..b913b60a 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/AttachmentMultiRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/AttachmentMultiRoutes.scala @@ -33,7 +33,7 @@ object AttachmentMultiRoutes extends NonEmptyListSupport { for { json <- req.as[IdList] attachments <- requireNonEmpty(json.ids) - n <- backend.item.deleteAttachmentMultiple(attachments, user.account.collective) + n <- backend.item.deleteAttachmentMultiple(attachments, user.account.collectiveId) res = BasicResult( n > 0, if (n > 0) "Attachment(s) deleted" else "Attachment deletion failed." diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/AttachmentRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/AttachmentRoutes.scala index 9d2264e0..39574043 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/AttachmentRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/AttachmentRoutes.scala @@ -8,7 +8,6 @@ package docspell.restserver.routes import cats.effect._ import cats.implicits._ - import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.backend.ops._ @@ -18,7 +17,7 @@ import docspell.restapi.model._ import docspell.restserver.conv.Conversions import docspell.restserver.http4s.BinaryUtil import docspell.restserver.webapp.Webjars - +import docspell.scheduler.usertask.UserTaskScope import org.http4s._ import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ @@ -45,19 +44,22 @@ object AttachmentRoutes { HttpRoutes.of { case HEAD -> Root / Ident(id) => for { - fileData <- backend.itemSearch.findAttachment(id, user.account.collective) + fileData <- backend.itemSearch.findAttachment(id, user.account.collectiveId) resp <- BinaryUtil.respondHead(dsl)(fileData) } yield resp case req @ GET -> Root / Ident(id) => for { - fileData <- backend.itemSearch.findAttachment(id, user.account.collective) + fileData <- backend.itemSearch.findAttachment(id, user.account.collectiveId) resp <- BinaryUtil.respond[F](dsl, req)(fileData) } yield resp case HEAD -> Root / Ident(id) / "original" => for { - fileData <- backend.itemSearch.findAttachmentSource(id, user.account.collective) + fileData <- backend.itemSearch.findAttachmentSource( + id, + user.account.collectiveId + ) resp <- fileData .map(data => withResponseHeaders(Ok())(data)) @@ -66,7 +68,10 @@ object AttachmentRoutes { case req @ GET -> Root / Ident(id) / "original" => for { - fileData <- backend.itemSearch.findAttachmentSource(id, user.account.collective) + fileData <- backend.itemSearch.findAttachmentSource( + id, + user.account.collectiveId + ) inm = req.headers.get[`If-None-Match`].flatMap(_.tags) matches = BinaryUtil.matchETag(fileData.map(_.meta), inm) resp <- @@ -81,7 +86,7 @@ object AttachmentRoutes { case HEAD -> Root / Ident(id) / "archive" => for { fileData <- - backend.itemSearch.findAttachmentArchive(id, user.account.collective) + backend.itemSearch.findAttachmentArchive(id, user.account.collectiveId) resp <- fileData .map(data => withResponseHeaders(Ok())(data)) @@ -91,7 +96,7 @@ object AttachmentRoutes { case req @ GET -> Root / Ident(id) / "archive" => for { fileData <- - backend.itemSearch.findAttachmentArchive(id, user.account.collective) + backend.itemSearch.findAttachmentArchive(id, user.account.collectiveId) inm = req.headers.get[`If-None-Match`].flatMap(_.tags) matches = BinaryUtil.matchETag(fileData.map(_.meta), inm) resp <- @@ -106,14 +111,14 @@ object AttachmentRoutes { case req @ GET -> Root / Ident(id) / "preview" => for { fileData <- - backend.itemSearch.findAttachmentPreview(id, user.account.collective) + backend.itemSearch.findAttachmentPreview(id, user.account.collectiveId) resp <- BinaryUtil.respondPreview(dsl, req)(fileData) } yield resp case HEAD -> Root / Ident(id) / "preview" => for { fileData <- - backend.itemSearch.findAttachmentPreview(id, user.account.collective) + backend.itemSearch.findAttachmentPreview(id, user.account.collectiveId) resp <- BinaryUtil.respondPreviewHead(dsl)(fileData) } yield resp @@ -121,7 +126,7 @@ object AttachmentRoutes { for { res <- backend.item.generatePreview( MakePreviewArgs.replace(id), - user.account + UserTaskScope(user.account) ) resp <- Ok( Conversions.basicResult(res, "Generating preview image task submitted.") @@ -138,7 +143,7 @@ object AttachmentRoutes { case GET -> Root / Ident(id) / "meta" => for { - rm <- backend.itemSearch.findAttachmentMeta(id, user.account.collective) + rm <- backend.itemSearch.findAttachmentMeta(id, user.account.collectiveId) md = rm.map(Conversions.mkAttachmentMeta) resp <- md.map(Ok(_)).getOrElse(NotFound(BasicResult(false, "Not found."))) } yield resp @@ -146,13 +151,13 @@ object AttachmentRoutes { case req @ POST -> Root / Ident(id) / "name" => for { nn <- req.as[OptionalText] - res <- backend.item.setAttachmentName(id, nn.text, user.account.collective) + res <- backend.item.setAttachmentName(id, nn.text, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Name updated.")) } yield resp case DELETE -> Root / Ident(id) => for { - n <- backend.item.deleteAttachment(id, user.account.collective) + n <- backend.item.deleteAttachment(id, user.account.collectiveId) res = if (n == 0) BasicResult(false, "Attachment not found") else BasicResult(true, "Attachment deleted.") @@ -177,10 +182,9 @@ object AttachmentRoutes { case POST -> Root / "convertallpdfs" => for { res <- - backend.item.convertAllPdf(None, None) + backend.item.convertAllPdf(None, UserTaskScope.system) resp <- Ok(Conversions.basicResult(res, "Convert all PDFs task submitted")) } yield resp } } - } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/CheckFileRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/CheckFileRoutes.scala index 1e471b48..e03dfe66 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/CheckFileRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/CheckFileRoutes.scala @@ -29,7 +29,7 @@ object CheckFileRoutes { HttpRoutes.of { case GET -> Root / checksum => for { items <- - backend.itemSearch.findByFileCollective(checksum, user.account.collective) + backend.itemSearch.findByFileCollective(checksum, user.account.collectiveId) resp <- Ok(convert(items)) } yield resp diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala index 86ea63b4..f259103c 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala @@ -36,7 +36,7 @@ object ClientSettingsRoutes { _ <- OptionT.liftF(logger.debug(s"Get client settings for share ${token.id}")) share <- backend.share.findActiveById(token.id) sett <- OptionT( - backend.clientSettings.loadCollective(clientId, share.user.accountId) + backend.clientSettings.loadCollective(clientId, share.account.collectiveId) ) res <- OptionT.liftF(Ok(sett.settingsData)) } yield res) @@ -51,7 +51,11 @@ object ClientSettingsRoutes { HttpRoutes.of { case GET -> Root / Ident(clientId) => for { - mergedData <- backend.clientSettings.loadMerged(clientId, user.account) + mergedData <- backend.clientSettings.loadMerged( + clientId, + user.account.collectiveId, + user.account.userId + ) res <- mergedData match { case Some(j) => Ok(j) case None => Ok(Map.empty[String, String]) @@ -61,13 +65,13 @@ object ClientSettingsRoutes { case req @ PUT -> Root / "user" / Ident(clientId) => for { data <- req.as[Json] - _ <- backend.clientSettings.saveUser(clientId, user.account, data) + _ <- backend.clientSettings.saveUser(clientId, user.account.userId, data) res <- Ok(BasicResult(true, "Settings stored")) } yield res case GET -> Root / "user" / Ident(clientId) => for { - data <- backend.clientSettings.loadUser(clientId, user.account) + data <- backend.clientSettings.loadUser(clientId, user.account.userId) res <- data match { case Some(d) => Ok(d.settingsData) case None => Ok(Map.empty[String, String]) @@ -76,7 +80,7 @@ object ClientSettingsRoutes { case DELETE -> Root / "user" / Ident(clientId) => for { - flag <- backend.clientSettings.deleteUser(clientId, user.account) + flag <- backend.clientSettings.deleteUser(clientId, user.account.userId) res <- Ok( BasicResult( flag, @@ -88,13 +92,20 @@ object ClientSettingsRoutes { case req @ PUT -> Root / "collective" / Ident(clientId) => for { data <- req.as[Json] - _ <- backend.clientSettings.saveCollective(clientId, user.account, data) + _ <- backend.clientSettings.saveCollective( + clientId, + user.account.collectiveId, + data + ) res <- Ok(BasicResult(true, "Settings stored")) } yield res case GET -> Root / "collective" / Ident(clientId) => for { - data <- backend.clientSettings.loadCollective(clientId, user.account) + data <- backend.clientSettings.loadCollective( + clientId, + user.account.collectiveId + ) res <- data match { case Some(d) => Ok(d.settingsData) case None => Ok(Map.empty[String, String]) @@ -103,7 +114,10 @@ object ClientSettingsRoutes { case DELETE -> Root / "collective" / Ident(clientId) => for { - flag <- backend.clientSettings.deleteCollective(clientId, user.account) + flag <- backend.clientSettings.deleteCollective( + clientId, + user.account.collectiveId + ) res <- Ok( BasicResult( flag, diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/CollectiveRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/CollectiveRoutes.scala index 8e4cc6e6..faedbac0 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/CollectiveRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/CollectiveRoutes.scala @@ -33,19 +33,19 @@ object CollectiveRoutes { case GET -> Root => for { collDb <- backend.collective.find(user.account.collective) - coll = collDb.map(c => Collective(c.id, c.state, c.created)) + coll = collDb.map(c => Collective(c.name, c.state, c.created)) resp <- coll.toResponse() } yield resp case GET -> Root / "insights" => for { - ins <- backend.collective.insights(user.account.collective) + ins <- backend.collective.insights(user.account.collectiveId) resp <- Ok(Conversions.mkItemInsights(ins)) } yield resp case GET -> Root / "tagcloud" => for { - cloud <- backend.collective.tagCloud(user.account.collective) + cloud <- backend.collective.tagCloud(user.account.collectiveId) resp <- Ok(Conversions.mkTagCloud(cloud)) } yield resp @@ -73,13 +73,13 @@ object CollectiveRoutes { ) res <- backend.collective - .updateSettings(user.account.collective, sett) + .updateSettings(user.account.collectiveId, sett) resp <- Ok(Conversions.basicResult(res, "Settings updated.")) } yield resp case GET -> Root / "settings" => for { - settDb <- backend.collective.findSettings(user.account.collective) + settDb <- backend.collective.findSettings(user.account.collectiveId) trash = settDb.flatMap(_.emptyTrash).getOrElse(OCollective.EmptyTrash.default) sett = settDb.map(c => CollectiveSettings( @@ -108,8 +108,8 @@ object CollectiveRoutes { for { res <- backend.collective - .getContacts(user.account.collective, q.map(_.q), kind) - .take(50) + .getContacts(user.account.collectiveId, q.map(_.q), kind) + .take(100) .compile .toList resp <- Ok(ContactList(res.map(Conversions.mkContact))) @@ -117,7 +117,7 @@ object CollectiveRoutes { case POST -> Root / "classifier" / "startonce" => for { - _ <- backend.collective.startLearnClassifier(user.account.collective) + _ <- backend.collective.startLearnClassifier(user.account.collectiveId) resp <- Ok(BasicResult(true, "Task submitted")) } yield resp @@ -125,11 +125,10 @@ object CollectiveRoutes { for { data <- req.as[EmptyTrashSetting] _ <- backend.collective.startEmptyTrash( - EmptyTrashArgs(user.account.collective, data.minAge) + EmptyTrashArgs(user.account.collectiveId, data.minAge) ) resp <- Ok(BasicResult(true, "Task submitted")) } yield resp } } - } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/CustomFieldRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/CustomFieldRoutes.scala index 2a32b0be..83492974 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/CustomFieldRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/CustomFieldRoutes.scala @@ -38,7 +38,7 @@ object CustomFieldRoutes { val order = sort.getOrElse(CustomFieldOrder.NameAsc) for { fs <- backend.customFields.findAll( - user.account.collective, + user.account.collectiveId, param.map(_.q), order ) @@ -54,7 +54,7 @@ object CustomFieldRoutes { case GET -> Root / Ident(id) => (for { - field <- OptionT(backend.customFields.findById(user.account.collective, id)) + field <- OptionT(backend.customFields.findById(user.account.collectiveId, id)) res <- OptionT.liftF(Ok(convertField(field))) } yield res).getOrElseF(NotFound(BasicResult(false, "Not found"))) @@ -67,7 +67,7 @@ object CustomFieldRoutes { case DELETE -> Root / Ident(id) => for { - res <- backend.customFields.delete(user.account.collective, id) + res <- backend.customFields.delete(user.account.collectiveId, id) resp <- Ok(convertResult(res)) } yield resp } @@ -88,7 +88,7 @@ object CustomFieldRoutes { id, in.name, in.label, - user.account.collective, + user.account.collectiveId, in.ftype, Timestamp.Epoch ) @@ -101,7 +101,7 @@ object CustomFieldRoutes { in.name, in.label, in.ftype, - user.account.collective + user.account.collectiveId ) private def convertField(f: CustomFieldData): CustomField = diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/DownloadAllRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/DownloadAllRoutes.scala index 3d549e08..65cf2b7c 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/DownloadAllRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/DownloadAllRoutes.scala @@ -75,7 +75,7 @@ object DownloadAllRoutes { case req @ GET -> Root / "file" / Ident(id) => for { - data <- backend.downloadAll.getFile(share.account.collective, id) + data <- backend.downloadAll.getFile(share.account.collectiveId, id) resp <- BinaryUtil.respond(dsl, req)(data) } yield resp } @@ -113,13 +113,13 @@ object DownloadAllRoutes { case req @ GET -> Root / "file" / Ident(id) => for { - data <- backend.downloadAll.getFile(token.account.collective, id) + data <- backend.downloadAll.getFile(token.account.collectiveId, id) resp <- BinaryUtil.respond(dsl, req)(data) } yield resp case HEAD -> Root / "file" / Ident(id) => for { - data <- backend.downloadAll.getFile(token.account.collective, id) + data <- backend.downloadAll.getFile(token.account.collectiveId, id) resp <- BinaryUtil.respondHead(dsl)(data) } yield resp @@ -131,7 +131,7 @@ object DownloadAllRoutes { case PUT -> Root / "cancel" / Ident(id) => for { - res <- backend.downloadAll.cancelDownload(token.account, id) + res <- backend.downloadAll.cancelDownload(token.account.collectiveId, id) resp <- Ok(Conversions.basicResult(res)) } yield resp } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/EquipmentRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/EquipmentRoutes.scala index ce9e3217..b2102a91 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/EquipmentRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/EquipmentRoutes.scala @@ -33,7 +33,7 @@ object EquipmentRoutes { case GET -> Root :? QueryParam.QueryOpt(q) :? QueryParam.EquipSort(sort) => for { data <- backend.equipment.findAll( - user.account, + user.account.collectiveId, q.map(_.q), sort.getOrElse(OEquipment.EquipmentOrder.NameAsc) ) @@ -43,7 +43,7 @@ object EquipmentRoutes { case req @ POST -> Root => for { data <- req.as[Equipment] - equip <- newEquipment(data, user.account.collective) + equip <- newEquipment(data, user.account.collectiveId) res <- backend.equipment.add(equip) resp <- Ok(basicResult(res, "Equipment created")) } yield resp @@ -51,20 +51,20 @@ object EquipmentRoutes { case req @ PUT -> Root => for { data <- req.as[Equipment] - equip <- changeEquipment(data, user.account.collective) + equip <- changeEquipment(data, user.account.collectiveId) res <- backend.equipment.update(equip) resp <- Ok(basicResult(res, "Equipment updated.")) } yield resp case DELETE -> Root / Ident(id) => for { - del <- backend.equipment.delete(id, user.account.collective) + del <- backend.equipment.delete(id, user.account.collectiveId) resp <- Ok(basicResult(del, "Equipment deleted.")) } yield resp case GET -> Root / Ident(id) => (for { - equip <- OptionT(backend.equipment.find(user.account, id)) + equip <- OptionT(backend.equipment.find(user.account.collectiveId, id)) resp <- OptionT.liftF(Ok(mkEquipment(equip))) } yield resp).getOrElseF(NotFound()) } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/FolderRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/FolderRoutes.scala index b177f3c8..463f20e1 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/FolderRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/FolderRoutes.scala @@ -35,9 +35,15 @@ object FolderRoutes { QueryParam.OwningOpt(owning) +& QueryParam.FolderSort(sort) => val order = sort.getOrElse(OFolder.FolderOrder.NameAsc) val login = - owning.filter(identity).map(_ => user.account.user) + owning.filter(identity).map(_ => user.account.login) for { - all <- backend.folder.findAll(user.account, login, q.map(_.q), order) + all <- backend.folder.findAll( + user.account.collectiveId, + user.account.userId, + login, + q.map(_.q), + order + ) resp <- Ok(FolderList(all.map(mkFolder).toList)) } yield resp @@ -45,46 +51,48 @@ object FolderRoutes { for { data <- req.as[NewFolder] nfolder <- newFolder(data, user.account) - res <- backend.folder.add(nfolder, Some(user.account.user)) + res <- backend.folder.add(nfolder, None) resp <- Ok(Conversions.idResult(res, nfolder.id, "Folder successfully created.")) } yield resp case GET -> Root / Ident(id) => (for { - folder <- OptionT(backend.folder.findById(id, user.account)) + folder <- OptionT( + backend.folder.findById(id, user.account.collectiveId, user.account.userId) + ) resp <- OptionT.liftF(Ok(mkFolderDetail(folder))) } yield resp).getOrElseF(NotFound()) case req @ PUT -> Root / Ident(id) => for { data <- req.as[NewFolder] - res <- backend.folder.changeName(id, user.account, data.name) + res <- backend.folder.changeName(id, user.account.userId, data.name) resp <- Ok(mkFolderChangeResult(res)) } yield resp case DELETE -> Root / Ident(id) => for { - res <- backend.folder.delete(id, user.account) + res <- backend.folder.delete(id, user.account.userId) resp <- Ok(mkFolderChangeResult(res)) } yield resp case PUT -> Root / Ident(id) / "member" / Ident(userId) => for { - res <- backend.folder.addMember(id, user.account, userId) + res <- backend.folder.addMember(id, user.account.userId, userId) resp <- Ok(mkFolderChangeResult(res)) } yield resp case DELETE -> Root / Ident(id) / "member" / Ident(userId) => for { - res <- backend.folder.removeMember(id, user.account, userId) + res <- backend.folder.removeMember(id, user.account.userId, userId) resp <- Ok(mkFolderChangeResult(res)) } yield resp } } - private def newFolder[F[_]: Sync](ns: NewFolder, account: AccountId): F[RFolder] = - RFolder.newFolder(ns.name, account) + private def newFolder[F[_]: Sync](ns: NewFolder, account: AccountInfo): F[RFolder] = + RFolder.newFolder(ns.name, account.collectiveId, account.userId) private def mkFolder(item: OFolder.FolderItem): FolderItem = FolderItem( diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/FullTextIndexRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/FullTextIndexRoutes.scala index 1a512693..4933f777 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/FullTextIndexRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/FullTextIndexRoutes.scala @@ -33,7 +33,9 @@ object FullTextIndexRoutes { HttpRoutes.of { case POST -> Root / "reIndex" => for { - res <- backend.fulltext.reindexCollective(user.account).attempt + res <- backend.fulltext + .reindexCollective(user.account.collectiveId, user.account.userId.some) + .attempt resp <- Ok(Conversions.basicResult(res, "Full-text index will be re-created.")) } yield resp } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/IntegrationEndpointRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/IntegrationEndpointRoutes.scala index 3777f18a..f95588b3 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/IntegrationEndpointRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/IntegrationEndpointRoutes.scala @@ -9,14 +9,12 @@ package docspell.restserver.routes import cats.data.{EitherT, OptionT} import cats.effect._ import cats.implicits._ - import docspell.backend.BackendApp import docspell.common._ import docspell.restserver.Config import docspell.restserver.conv.Conversions._ import docspell.restserver.http4s.Responses -import docspell.store.records.RItem - +import docspell.store.records.{RCollective, RItem} import org.http4s._ import org.http4s.circe.CirceEntityEncoder._ import org.http4s.dsl.Http4sDsl @@ -36,15 +34,15 @@ object IntegrationEndpointRoutes { for { _ <- authRequest(req, cfg.integrationEndpoint) _ <- checkEnabled(cfg.integrationEndpoint) - _ <- lookupCollective(collective, backend) - } yield () + c <- lookupCollective(collective, backend) + } yield c HttpRoutes.of { case req @ POST -> Root / "item" / Ident(collective) => (for { - _ <- validate(req, collective) + coll <- validate(req, collective) res <- EitherT.liftF[F, Response[F], Response[F]]( - uploadFile(collective, backend, cfg, dsl)(req) + uploadFile(coll.id, backend, cfg, dsl)(req) ) } yield res).fold(identity, identity) @@ -56,9 +54,9 @@ object IntegrationEndpointRoutes { case req @ GET -> Root / "checkfile" / Ident(collective) / checksum => (for { - _ <- validate(req, collective) + coll <- validate(req, collective) items <- EitherT.liftF[F, Response[F], Vector[RItem]]( - backend.itemSearch.findByFileCollective(checksum, collective) + backend.itemSearch.findByFileCollective(checksum, coll.id) ) resp <- EitherT.liftF[F, Response[F], Response[F]](Ok(CheckFileRoutes.convert(items))) @@ -86,14 +84,13 @@ object IntegrationEndpointRoutes { def lookupCollective[F[_]: Async]( coll: Ident, backend: BackendApp[F] - ): EitherT[F, Response[F], Unit] = - for { - opt <- EitherT.liftF(backend.collective.find(coll)) - res <- EitherT.cond[F](opt.exists(_.integrationEnabled), (), Response.notFound[F]) - } yield res + ): EitherT[F, Response[F], RCollective] = + OptionT(backend.collective.find(coll)) + .filter(_.integrationEnabled) + .toRight(Response.notFound[F]) def uploadFile[F[_]: Async]( - coll: Ident, + cid: CollectiveId, backend: BackendApp[F], cfg: Config, dsl: Http4sDsl[F] @@ -110,8 +107,7 @@ object IntegrationEndpointRoutes { cfg.integrationEndpoint.priority, cfg.backend.files.validMimeTypes ) - account = AccountId(coll, DocspellSystem.user) - result <- backend.upload.submit(updata, account, None) + result <- backend.upload.submit(updata, cid, None, None) res <- Ok(basicResult(result)) } yield res } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemLinkRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemLinkRoutes.scala index 2469c99e..097f162e 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemLinkRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemLinkRoutes.scala @@ -22,7 +22,7 @@ import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityCodec._ import org.http4s.dsl.Http4sDsl -class ItemLinkRoutes[F[_]: Async](account: AccountId, backend: OItemLink[F]) +class ItemLinkRoutes[F[_]: Async](account: AccountInfo, backend: OItemLink[F]) extends Http4sDsl[F] { def get: HttpRoutes[F] = HttpRoutes.of { @@ -36,7 +36,7 @@ class ItemLinkRoutes[F[_]: Async](account: AccountId, backend: OItemLink[F]) case DELETE -> Root / Ident(target) / Ident(id) => for { - _ <- backend.removeAll(account.collective, target, NonEmptyList.of(id)) + _ <- backend.removeAll(account.collectiveId, target, NonEmptyList.of(id)) resp <- Ok(BasicResult(true, "Related items removed")) } yield resp @@ -46,7 +46,7 @@ class ItemLinkRoutes[F[_]: Async](account: AccountId, backend: OItemLink[F]) related = NonEmptyList.fromList(input.related) res <- OptionT .fromOption[F](related) - .semiflatMap(backend.addAll(account.collective, input.item, _)) + .semiflatMap(backend.addAll(account.collectiveId, input.item, _)) .value resp <- Ok(convertResult(res)) } yield resp @@ -56,7 +56,7 @@ class ItemLinkRoutes[F[_]: Async](account: AccountId, backend: OItemLink[F]) input <- req.as[ItemLinkData] related = NonEmptyList.fromList(input.related) _ <- related - .map(backend.removeAll(account.collective, input.item, _)) + .map(backend.removeAll(account.collectiveId, input.item, _)) .getOrElse( BadRequest(BasicResult(false, "List of related items must not be empty")) ) @@ -77,6 +77,6 @@ class ItemLinkRoutes[F[_]: Async](account: AccountId, backend: OItemLink[F]) object ItemLinkRoutes { - def apply[F[_]: Async](account: AccountId, itemLink: OItemLink[F]): HttpRoutes[F] = + def apply[F[_]: Async](account: AccountInfo, itemLink: OItemLink[F]): HttpRoutes[F] = new ItemLinkRoutes[F](account, itemLink).get } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala index 1a8ebd6a..cc65ac0c 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemMultiRoutes.scala @@ -8,7 +8,6 @@ package docspell.restserver.routes import cats.effect._ import cats.implicits._ - import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.backend.ops.OCustomFields.{RemoveValue, SetValue} @@ -17,7 +16,7 @@ import docspell.restapi.model._ import docspell.restserver.Config import docspell.restserver.conv.{Conversions, MultiIdSupport, NonEmptyListSupport} import docspell.restserver.http4s.ClientRequestInfo - +import docspell.scheduler.usertask.UserTaskScope import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ @@ -42,7 +41,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { res <- backend.item.setStates( data, ItemState.Confirmed, - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Item data confirmed")) } yield resp @@ -54,7 +53,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { res <- backend.item.setStates( data, ItemState.Created, - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Item back to created.")) } yield resp @@ -66,7 +65,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { res <- backend.item.setTagsMultipleItems( items, json.refs, - user.account.collective + user.account.collectiveId ) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) @@ -80,7 +79,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { res <- backend.item.linkTagsMultipleItems( items, json.refs, - user.account.collective + user.account.collectiveId ) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) @@ -94,7 +93,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { res <- backend.item.removeTagsMultipleItems( items, json.refs, - user.account.collective + user.account.collectiveId ) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) @@ -108,7 +107,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { res <- backend.item.setNameMultiple( items, json.name.notEmpty.getOrElse(""), - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Name updated")) } yield resp @@ -120,7 +119,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { res <- backend.item.setFolderMultiple( items, json.ref.map(_.id), - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Folder updated")) } yield resp @@ -129,7 +128,11 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[ItemsAndDirection] items <- requireNonEmpty(json.items) - res <- backend.item.setDirection(items, json.direction, user.account.collective) + res <- backend.item.setDirection( + items, + json.direction, + user.account.collectiveId + ) resp <- Ok(Conversions.basicResult(res, "Direction updated")) } yield resp @@ -137,7 +140,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[ItemsAndDate] items <- requireNonEmpty(json.items) - res <- backend.item.setItemDate(items, json.date, user.account.collective) + res <- backend.item.setItemDate(items, json.date, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Item date updated")) } yield resp @@ -145,7 +148,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[ItemsAndDate] items <- requireNonEmpty(json.items) - res <- backend.item.setItemDueDate(items, json.date, user.account.collective) + res <- backend.item.setItemDueDate(items, json.date, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Item due date updated")) } yield resp @@ -153,7 +156,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[ItemsAndRef] items <- requireNonEmpty(json.items) - res <- backend.item.setCorrOrg(items, json.ref, user.account.collective) + res <- backend.item.setCorrOrg(items, json.ref, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Correspondent organization updated")) } yield resp @@ -161,7 +164,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[ItemsAndRef] items <- requireNonEmpty(json.items) - res <- backend.item.setCorrPerson(items, json.ref, user.account.collective) + res <- backend.item.setCorrPerson(items, json.ref, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Correspondent person updated")) } yield resp @@ -169,7 +172,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[ItemsAndRef] items <- requireNonEmpty(json.items) - res <- backend.item.setConcPerson(items, json.ref, user.account.collective) + res <- backend.item.setConcPerson(items, json.ref, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Concerned person updated")) } yield resp @@ -177,7 +180,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[ItemsAndRef] items <- requireNonEmpty(json.items) - res <- backend.item.setConcEquip(items, json.ref, user.account.collective) + res <- backend.item.setConcEquip(items, json.ref, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Concerned equipment updated")) } yield resp @@ -185,7 +188,11 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[IdList] items <- requireNonEmpty(json.ids) - res <- backend.item.reprocessAll(items, user.account) + res <- backend.item.reprocessAll( + user.account.collectiveId, + items, + UserTaskScope(user.account) + ) resp <- Ok(Conversions.basicResult(res, "Re-process task(s) submitted.")) } yield resp @@ -193,7 +200,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[IdList] items <- requireNonEmpty(json.ids) - n <- backend.item.setDeletedState(items, user.account.collective) + n <- backend.item.setDeletedState(items, user.account.collectiveId) res = BasicResult( n > 0, if (n > 0) "Item(s) deleted" else "Item deletion failed." @@ -205,7 +212,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[IdList] items <- requireNonEmpty(json.ids) - res <- backend.item.restore(items, user.account.collective) + res <- backend.item.restore(items, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Item(s) deleted")) } yield resp @@ -215,7 +222,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { items <- requireNonEmpty(json.items) res <- backend.customFields.setValueMultiple( items, - SetValue(json.field.field, json.field.value, user.account.collective) + SetValue(json.field.field, json.field.value, user.account.collectiveId) ) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) @@ -228,7 +235,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { items <- requireNonEmpty(json.items) field <- readId[F](json.name) res <- backend.customFields.deleteValue( - RemoveValue(field, items, user.account.collective) + RemoveValue(field, items, user.account.collectiveId) ) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) @@ -239,7 +246,7 @@ object ItemMultiRoutes extends NonEmptyListSupport with MultiIdSupport { for { json <- req.as[IdList] items <- requireNonEmpty(json.ids) - res <- backend.item.merge(logger, items, user.account.collective) + res <- backend.item.merge(logger, items, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Items merged")) } yield resp } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala index 14dd23c9..57db1d9e 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ItemRoutes.scala @@ -9,7 +9,6 @@ package docspell.restserver.routes import cats.data.NonEmptyList import cats.effect._ import cats.implicits._ - import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.backend.ops.OCustomFields.{RemoveValue, SetValue} @@ -21,7 +20,7 @@ import docspell.restserver.http4s.BinaryUtil import docspell.restserver.http4s.ClientRequestInfo import docspell.restserver.http4s.Responses import docspell.restserver.http4s.{QueryParam => QP} - +import docspell.scheduler.usertask.UserTaskScope import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ @@ -43,7 +42,7 @@ object ItemRoutes { HttpRoutes.of { case GET -> Root / Ident(id) => for { - item <- backend.itemSearch.findItem(id, user.account.collective) + item <- backend.itemSearch.findItem(id, user.account.collectiveId) result = item.map(Conversions.mkItemDetail) resp <- result @@ -53,26 +52,30 @@ object ItemRoutes { case POST -> Root / Ident(id) / "confirm" => for { - res <- backend.item.setState(id, ItemState.Confirmed, user.account.collective) + res <- backend.item.setState( + id, + ItemState.Confirmed, + user.account.collectiveId + ) resp <- Ok(Conversions.basicResult(res, "Item data confirmed")) } yield resp case POST -> Root / Ident(id) / "unconfirm" => for { - res <- backend.item.setState(id, ItemState.Created, user.account.collective) + res <- backend.item.setState(id, ItemState.Created, user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Item back to created.")) } yield resp case POST -> Root / Ident(id) / "restore" => for { - res <- backend.item.restore(NonEmptyList.of(id), user.account.collective) + res <- backend.item.restore(NonEmptyList.of(id), user.account.collectiveId) resp <- Ok(Conversions.basicResult(res, "Item restored.")) } yield resp case req @ PUT -> Root / Ident(id) / "tags" => for { tags <- req.as[StringList].map(_.items) - res <- backend.item.setTags(id, tags, user.account.collective) + res <- backend.item.setTags(id, tags, user.account.collectiveId) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) resp <- Ok(Conversions.basicResult(res.value, "Tags updated")) @@ -81,8 +84,8 @@ object ItemRoutes { case req @ POST -> Root / Ident(id) / "tags" => for { data <- req.as[Tag] - rtag <- Conversions.newTag(data, user.account.collective) - res <- backend.item.addNewTag(user.account.collective, id, rtag) + rtag <- Conversions.newTag(data, user.account.collectiveId) + res <- backend.item.addNewTag(user.account.collectiveId, id, rtag) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) resp <- Ok(Conversions.basicResult(res.value, "Tag added.")) @@ -91,7 +94,7 @@ object ItemRoutes { case req @ PUT -> Root / Ident(id) / "taglink" => for { tags <- req.as[StringList] - res <- backend.item.linkTags(id, tags.items, user.account.collective) + res <- backend.item.linkTags(id, tags.items, user.account.collectiveId) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) resp <- Ok(Conversions.basicResult(res.value, "Tags linked")) @@ -100,7 +103,7 @@ object ItemRoutes { case req @ POST -> Root / Ident(id) / "tagtoggle" => for { tags <- req.as[StringList] - res <- backend.item.toggleTags(id, tags.items, user.account.collective) + res <- backend.item.toggleTags(id, tags.items, user.account.collectiveId) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) resp <- Ok(Conversions.basicResult(res.value, "Tags linked")) @@ -112,7 +115,7 @@ object ItemRoutes { res <- backend.item.removeTagsMultipleItems( NonEmptyList.of(id), json.items, - user.account.collective + user.account.collectiveId ) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) @@ -125,7 +128,7 @@ object ItemRoutes { res <- backend.item.setDirection( NonEmptyList.of(id), dir.direction, - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Direction updated")) } yield resp @@ -133,7 +136,11 @@ object ItemRoutes { case req @ PUT -> Root / Ident(id) / "folder" => for { idref <- req.as[OptionalId] - res <- backend.item.setFolder(id, idref.id.map(_.id), user.account.collective) + res <- backend.item.setFolder( + id, + idref.id.map(_.id), + user.account.collectiveId + ) resp <- Ok(Conversions.basicResult(res, "Folder updated")) } yield resp @@ -143,7 +150,7 @@ object ItemRoutes { res <- backend.item.setCorrOrg( NonEmptyList.of(id), idref.id, - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Correspondent organization updated")) } yield resp @@ -151,7 +158,7 @@ object ItemRoutes { case req @ POST -> Root / Ident(id) / "corrOrg" => for { data <- req.as[Organization] - org <- Conversions.newOrg(data, user.account.collective) + org <- Conversions.newOrg(data, user.account.collectiveId) res <- backend.item.addCorrOrg(id, org) resp <- Ok(Conversions.basicResult(res, "Correspondent organization updated")) } yield resp @@ -162,7 +169,7 @@ object ItemRoutes { res <- backend.item.setCorrPerson( NonEmptyList.of(id), idref.id, - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Correspondent person updated")) } yield resp @@ -170,7 +177,7 @@ object ItemRoutes { case req @ POST -> Root / Ident(id) / "corrPerson" => for { data <- req.as[Person] - pers <- Conversions.newPerson(data, user.account.collective) + pers <- Conversions.newPerson(data, user.account.collectiveId) res <- backend.item.addCorrPerson(id, pers) resp <- Ok(Conversions.basicResult(res, "Correspondent person updated")) } yield resp @@ -181,7 +188,7 @@ object ItemRoutes { res <- backend.item.setConcPerson( NonEmptyList.of(id), idref.id, - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Concerned person updated")) } yield resp @@ -189,7 +196,7 @@ object ItemRoutes { case req @ POST -> Root / Ident(id) / "concPerson" => for { data <- req.as[Person] - pers <- Conversions.newPerson(data, user.account.collective) + pers <- Conversions.newPerson(data, user.account.collectiveId) res <- backend.item.addConcPerson(id, pers) resp <- Ok(Conversions.basicResult(res, "Concerned person updated")) } yield resp @@ -200,7 +207,7 @@ object ItemRoutes { res <- backend.item.setConcEquip( NonEmptyList.of(id), idref.id, - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Concerned equipment updated")) } yield resp @@ -208,7 +215,7 @@ object ItemRoutes { case req @ POST -> Root / Ident(id) / "concEquipment" => for { data <- req.as[Equipment] - equip <- Conversions.newEquipment(data, user.account.collective) + equip <- Conversions.newEquipment(data, user.account.collectiveId) res <- backend.item.addConcEquip(id, equip) resp <- Ok(Conversions.basicResult(res, "Concerned equipment updated")) } yield resp @@ -216,7 +223,11 @@ object ItemRoutes { case req @ PUT -> Root / Ident(id) / "notes" => for { text <- req.as[OptionalText] - res <- backend.item.setNotes(id, text.text.notEmpty, user.account.collective) + res <- backend.item.setNotes( + id, + text.text.notEmpty, + user.account.collectiveId + ) resp <- Ok(Conversions.basicResult(res, "Notes updated")) } yield resp @@ -226,7 +237,7 @@ object ItemRoutes { res <- backend.item.setName( id, text.text.notEmpty.getOrElse(""), - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Name updated")) } yield resp @@ -238,7 +249,7 @@ object ItemRoutes { res <- backend.item.setItemDueDate( NonEmptyList.of(id), date.date, - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Item due date updated")) } yield resp @@ -250,14 +261,14 @@ object ItemRoutes { res <- backend.item.setItemDate( NonEmptyList.of(id), date.date, - user.account.collective + user.account.collectiveId ) resp <- Ok(Conversions.basicResult(res, "Item date updated")) } yield resp case GET -> Root / Ident(id) / "proposals" => for { - ml <- backend.item.getProposals(id, user.account.collective) + ml <- backend.item.getProposals(id, user.account.collectiveId) ip = Conversions.mkItemProposals(ml) resp <- Ok(ip) } yield resp @@ -274,7 +285,7 @@ object ItemRoutes { def notFound = NotFound(BasicResult(false, "Not found")) for { - preview <- backend.itemSearch.findItemPreview(id, user.account.collective) + preview <- backend.itemSearch.findItemPreview(id, user.account.collectiveId) inm = req.headers.get[`If-None-Match`].flatMap(_.tags) matches = BinaryUtil.matchETag(preview.map(_.meta), inm) fallback = flag.getOrElse(false) @@ -292,7 +303,7 @@ object ItemRoutes { case HEAD -> Root / Ident(id) / "preview" => for { - preview <- backend.itemSearch.findItemPreview(id, user.account.collective) + preview <- backend.itemSearch.findItemPreview(id, user.account.collectiveId) resp <- preview .map(data => BinaryUtil.withResponseHeaders(dsl, Ok())(data)) @@ -303,7 +314,12 @@ object ItemRoutes { for { data <- req.as[IdList] _ <- logger.debug(s"Re-process item ${id.id}") - res <- backend.item.reprocess(id, data.ids, user.account) + res <- backend.item.reprocess( + user.account.collectiveId, + id, + data.ids, + UserTaskScope(user.account) + ) resp <- Ok(Conversions.basicResult(res, "Re-process task submitted.")) } yield resp @@ -312,7 +328,7 @@ object ItemRoutes { data <- req.as[CustomFieldValue] res <- backend.customFields.setValue( id, - SetValue(data.field, data.value, user.account.collective) + SetValue(data.field, data.value, user.account.collectiveId) ) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) @@ -322,7 +338,7 @@ object ItemRoutes { case req @ DELETE -> Root / Ident(id) / "customfield" / Ident(fieldId) => for { res <- backend.customFields.deleteValue( - RemoveValue(fieldId, NonEmptyList.of(id), user.account.collective) + RemoveValue(fieldId, NonEmptyList.of(id), user.account.collectiveId) ) baseUrl = ClientRequestInfo.getBaseUrl(cfg, req) _ <- backend.notification.offerEvents(res.event(user.account, baseUrl.some)) @@ -333,7 +349,7 @@ object ItemRoutes { for { n <- backend.item.setDeletedState( NonEmptyList.of(id), - user.account.collective + user.account.collectiveId ) res = BasicResult( n > 0, diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/JobQueueRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/JobQueueRoutes.scala index 21e0ca53..25841f25 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/JobQueueRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/JobQueueRoutes.scala @@ -8,13 +8,12 @@ package docspell.restserver.routes import cats.effect._ import cats.implicits._ - import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.common.Ident import docspell.restapi.model.JobPriority import docspell.restserver.conv.Conversions - +import docspell.scheduler.usertask.UserTaskScope import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ @@ -29,21 +28,21 @@ object JobQueueRoutes { HttpRoutes.of { case GET -> Root / "state" => for { - js <- backend.job.queueState(user.account.collective, 40) + js <- backend.job.queueState(UserTaskScope(user.account), 40) res = Conversions.mkJobQueueState(js) resp <- Ok(res) } yield resp case POST -> Root / Ident(id) / "cancel" => for { - result <- backend.job.cancelJob(id, user.account.collective) + result <- backend.job.cancelJob(id, UserTaskScope(user.account)) resp <- Ok(Conversions.basicResult(result)) } yield resp case req @ POST -> Root / Ident(id) / "priority" => for { prio <- req.as[JobPriority] - res <- backend.job.setPriority(id, user.account.collective, prio.priority) + res <- backend.job.setPriority(id, UserTaskScope(user.account), prio.priority) resp <- Ok(Conversions.basicResult(res, "Job priority changed")) } yield resp } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/LoginRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/LoginRoutes.scala index fa7cab5b..464bd149 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/LoginRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/LoginRoutes.scala @@ -96,7 +96,7 @@ object LoginRoutes { resp <- Ok( AuthResult( token.account.collective.id, - token.account.user.id, + token.account.login.id, true, "Login successful", Some(cd.asString), diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/MailSendRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/MailSendRoutes.scala index f0e44909..15ceceac 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/MailSendRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/MailSendRoutes.scala @@ -32,7 +32,9 @@ object MailSendRoutes { for { in <- req.as[SimpleMail] mail = convertIn(id, in) - res <- mail.traverse(m => backend.mail.sendMail(user.account, name, m)) + res <- mail.traverse(m => + backend.mail.sendMail(user.account.userId, user.account.collectiveId, name, m) + ) resp <- res.fold( err => Ok(BasicResult(false, s"Invalid mail data: $err")), res => Ok(convertOut(res)) diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/MailSettingsRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/MailSettingsRoutes.scala index 4f0cd169..ff391d67 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/MailSettingsRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/MailSettingsRoutes.scala @@ -35,27 +35,27 @@ object MailSettingsRoutes { HttpRoutes.of { case GET -> Root / "smtp" :? QueryParam.QueryOpt(q) => for { - list <- backend.mail.getSmtpSettings(user.account, q.map(_.q)) + list <- backend.mail.getSmtpSettings(user.account.userId, q.map(_.q)) res = list.map(convert) resp <- Ok(EmailSettingsList(res.toList)) } yield resp case GET -> Root / "imap" :? QueryParam.QueryOpt(q) => for { - list <- backend.mail.getImapSettings(user.account, q.map(_.q)) + list <- backend.mail.getImapSettings(user.account.userId, q.map(_.q)) res = list.map(convert) resp <- Ok(ImapSettingsList(res.toList)) } yield resp case GET -> Root / "smtp" / Ident(name) => (for { - ems <- backend.mail.findSmtpSettings(user.account, name) + ems <- backend.mail.findSmtpSettings(user.account.userId, name) resp <- OptionT.liftF(Ok(convert(ems))) } yield resp).getOrElseF(NotFound()) case GET -> Root / "imap" / Ident(name) => (for { - ems <- backend.mail.findImapSettings(user.account, name) + ems <- backend.mail.findImapSettings(user.account.userId, name) resp <- OptionT.liftF(Ok(convert(ems))) } yield resp).getOrElseF(NotFound()) @@ -64,7 +64,7 @@ object MailSettingsRoutes { in <- OptionT.liftF(req.as[EmailSettings]) ru = makeSmtpSettings(in) up <- OptionT.liftF( - ru.traverse(r => backend.mail.createSmtpSettings(user.account, r)) + ru.traverse(r => backend.mail.createSmtpSettings(user.account.userId, r)) ) resp <- OptionT.liftF( Ok( @@ -81,7 +81,7 @@ object MailSettingsRoutes { in <- OptionT.liftF(req.as[ImapSettings]) ru = makeImapSettings(in) up <- OptionT.liftF( - ru.traverse(r => backend.mail.createImapSettings(user.account, r)) + ru.traverse(r => backend.mail.createImapSettings(user.account.userId, r)) ) resp <- OptionT.liftF( Ok( @@ -98,7 +98,9 @@ object MailSettingsRoutes { in <- OptionT.liftF(req.as[EmailSettings]) ru = makeSmtpSettings(in) up <- OptionT.liftF( - ru.traverse(r => backend.mail.updateSmtpSettings(user.account, name, r)) + ru.traverse(r => + backend.mail.updateSmtpSettings(user.account.userId, name, r) + ) ) resp <- OptionT.liftF( Ok( @@ -117,7 +119,9 @@ object MailSettingsRoutes { in <- OptionT.liftF(req.as[ImapSettings]) ru = makeImapSettings(in) up <- OptionT.liftF( - ru.traverse(r => backend.mail.updateImapSettings(user.account, name, r)) + ru.traverse(r => + backend.mail.updateImapSettings(user.account.userId, name, r) + ) ) resp <- OptionT.liftF( Ok( @@ -133,7 +137,7 @@ object MailSettingsRoutes { case DELETE -> Root / "smtp" / Ident(name) => for { - n <- backend.mail.deleteSmtpSettings(user.account, name) + n <- backend.mail.deleteSmtpSettings(user.account.userId, name) resp <- Ok( if (n > 0) BasicResult(true, "Mail settings removed") else BasicResult(false, "Mail settings could not be removed") @@ -142,7 +146,7 @@ object MailSettingsRoutes { case DELETE -> Root / "imap" / Ident(name) => for { - n <- backend.mail.deleteImapSettings(user.account, name) + n <- backend.mail.deleteImapSettings(user.account.userId, name) resp <- Ok( if (n > 0) BasicResult(true, "Mail settings removed") else BasicResult(false, "Mail settings could not be removed") diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/NotificationRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/NotificationRoutes.scala index e0c9f0bd..2e5940d0 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/NotificationRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/NotificationRoutes.scala @@ -50,14 +50,14 @@ object NotificationRoutes extends NonEmptyListSupport { HttpRoutes.of { case GET -> Root => for { - list <- backend.notification.listChannels(user.account) + list <- backend.notification.listChannels(user.account.userId) data = list.map(NotificationChannel.convert) resp <- Ok(data) } yield resp case DELETE -> Root / Ident(id) => for { - res <- backend.notification.deleteChannel(id, user.account) + res <- backend.notification.deleteChannel(id, user.account.userId) resp <- Ok(Conversions.basicResult(res, "Channel deleted")) } yield resp @@ -69,7 +69,7 @@ object NotificationRoutes extends NonEmptyListSupport { .fromEither[F](ch) .semiflatMap { c => backend.notification - .createChannel(c, user.account) + .createChannel(c, user.account.userId) .map(res => Conversions.basicResult(res, "Channel created")) } .foldF(ex => BadRequest(BasicResult(false, ex.getMessage)), Ok(_)) @@ -83,7 +83,7 @@ object NotificationRoutes extends NonEmptyListSupport { .fromEither[F](ch) .semiflatMap { c => backend.notification - .updateChannel(c, user.account) + .updateChannel(c, user.account.userId) .map(res => Conversions.basicResult(res, "Channel created")) } .foldF(ex => BadRequest(BasicResult(false, ex.getMessage)), Ok(_)) @@ -102,14 +102,14 @@ object NotificationRoutes extends NonEmptyListSupport { HttpRoutes.of { case GET -> Root => for { - list <- backend.notification.listHooks(user.account) + list <- backend.notification.listHooks(user.account.userId) data = list.map(Converters.convertHook) resp <- Ok(data) } yield resp case DELETE -> Root / Ident(id) => for { - res <- backend.notification.deleteHook(id, user.account) + res <- backend.notification.deleteHook(id, user.account.userId) resp <- Ok(Conversions.basicResult(res, "Hook deleted.")) } yield resp @@ -117,7 +117,7 @@ object NotificationRoutes extends NonEmptyListSupport { for { input <- req.as[NotificationHook] hook <- Sync[F].pure(Converters.convertHook(input)).rethrow - res <- backend.notification.createHook(hook, user.account) + res <- backend.notification.createHook(hook, user.account.userId) resp <- Ok(Conversions.basicResult(res, "Hook created")) } yield resp @@ -125,7 +125,7 @@ object NotificationRoutes extends NonEmptyListSupport { for { input <- req.as[NotificationHook] hook <- Sync[F].pure(Converters.convertHook(input)).rethrow - res <- backend.notification.updateHook(hook, user.account) + res <- backend.notification.updateHook(hook, user.account.userId) resp <- Ok(Conversions.basicResult(res, "Hook updated")) } yield resp @@ -187,7 +187,7 @@ object NotificationRoutes extends NonEmptyListSupport { NotificationHook( h.id, h.enabled, - h.channels.map(c => NotificationChannelRef(c.id, c.channelType, c.name)).toList, + h.channels.map(c => NotificationChannelRef(c.id, c.channelType, c.name)), h.allEvents, h.eventFilter, h.events diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/NotifyDueItemsRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/NotifyDueItemsRoutes.scala index dc180ba0..c09cd1f4 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/NotifyDueItemsRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/NotifyDueItemsRoutes.scala @@ -110,7 +110,7 @@ object NotifyDueItemsRoutes extends MailAddressCodec with NonEmptyListSupport { def makeTask[F[_]: Sync]( id: Ident, baseUrl: LenientUri, - user: AccountId, + user: AccountInfo, settings: PeriodicDueItemsSettings ): F[UserTask[PeriodicDueItemsArgs]] = requireNonEmpty(settings.channels).map { ch => diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/OrganizationRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/OrganizationRoutes.scala index 25d0218b..0f8536e4 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/OrganizationRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/OrganizationRoutes.scala @@ -36,7 +36,7 @@ object OrganizationRoutes { if (full.getOrElse(false)) for { data <- backend.organization.findAllOrg( - user.account, + user.account.collectiveId, q.map(_.q), order ) @@ -44,14 +44,18 @@ object OrganizationRoutes { } yield resp else for { - data <- backend.organization.findAllOrgRefs(user.account, q.map(_.q), order) + data <- backend.organization.findAllOrgRefs( + user.account.collectiveId, + q.map(_.q), + order + ) resp <- Ok(ReferenceList(data.map(mkIdName).toList)) } yield resp case req @ POST -> Root => for { data <- req.as[Organization] - newOrg <- newOrg(data, user.account.collective) + newOrg <- newOrg(data, user.account.collectiveId) added <- backend.organization.addOrg(newOrg) resp <- Ok(basicResult(added, "New organization saved.")) } yield resp @@ -59,23 +63,22 @@ object OrganizationRoutes { case req @ PUT -> Root => for { data <- req.as[Organization] - upOrg <- changeOrg(data, user.account.collective) + upOrg <- changeOrg(data, user.account.collectiveId) update <- backend.organization.updateOrg(upOrg) resp <- Ok(basicResult(update, "Organization updated.")) } yield resp case DELETE -> Root / Ident(id) => for { - delOrg <- backend.organization.deleteOrg(id, user.account.collective) + delOrg <- backend.organization.deleteOrg(id, user.account.collectiveId) resp <- Ok(basicResult(delOrg, "Organization deleted.")) } yield resp case GET -> Root / Ident(id) => (for { - org <- OptionT(backend.organization.findOrg(user.account, id)) + org <- OptionT(backend.organization.findOrg(user.account.collectiveId, id)) resp <- OptionT.liftF(Ok(mkOrg(org))) } yield resp).getOrElseF(NotFound()) } } - } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/PeriodicQueryRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/PeriodicQueryRoutes.scala index 0a300130..0245216a 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/PeriodicQueryRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/PeriodicQueryRoutes.scala @@ -112,7 +112,7 @@ object PeriodicQueryRoutes extends MailAddressCodec with NonEmptyListSupport { def makeTask[F[_]: Sync]( id: Ident, baseUrl: LenientUri, - user: AccountId, + user: AccountInfo, settings: PeriodicQuerySettings ): F[UserTask[PeriodicQueryArgs]] = Sync[F] diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/PersonRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/PersonRoutes.scala index 0f893571..6e033754 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/PersonRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/PersonRoutes.scala @@ -37,7 +37,7 @@ object PersonRoutes { if (full.getOrElse(false)) for { data <- backend.organization.findAllPerson( - user.account, + user.account.collectiveId, q.map(_.q), order ) @@ -46,7 +46,7 @@ object PersonRoutes { else for { data <- backend.organization.findAllPersonRefs( - user.account, + user.account.collectiveId, q.map(_.q), order ) @@ -56,7 +56,7 @@ object PersonRoutes { case req @ POST -> Root => for { data <- req.as[Person] - newPer <- newPerson(data, user.account.collective) + newPer <- newPerson(data, user.account.collectiveId) added <- backend.organization.addPerson(newPer) resp <- Ok(basicResult(added, "New person saved.")) } yield resp @@ -64,7 +64,7 @@ object PersonRoutes { case req @ PUT -> Root => for { data <- req.as[Person] - upPer <- changePerson(data, user.account.collective) + upPer <- changePerson(data, user.account.collectiveId) update <- backend.organization.updatePerson(upPer) resp <- Ok(basicResult(update, "Person updated.")) } yield resp @@ -72,16 +72,15 @@ object PersonRoutes { case DELETE -> Root / Ident(id) => for { _ <- logger.debug(s"Deleting person ${id.id}") - delOrg <- backend.organization.deletePerson(id, user.account.collective) + delOrg <- backend.organization.deletePerson(id, user.account.collectiveId) resp <- Ok(basicResult(delOrg, "Person deleted.")) } yield resp case GET -> Root / Ident(id) => (for { - org <- OptionT(backend.organization.findPerson(user.account, id)) + org <- OptionT(backend.organization.findPerson(user.account.collectiveId, id)) resp <- OptionT.liftF(Ok(mkPerson(org))) } yield resp).getOrElseF(NotFound()) } } - } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ScanMailboxRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ScanMailboxRoutes.scala index 61f27c11..563eb795 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ScanMailboxRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ScanMailboxRoutes.scala @@ -35,7 +35,7 @@ object ScanMailboxRoutes { HttpRoutes.of { case GET -> Root => ut.getScanMailbox(UserTaskScope(user.account)) - .evalMap(task => taskToSettings(user.account, backend, task)) + .evalMap(task => taskToSettings(user.account.userId, backend, task)) .compile .toVector .map(v => ScanMailboxSettingsList(v.toList)) @@ -44,7 +44,7 @@ object ScanMailboxRoutes { case GET -> Root / Ident(id) => (for { task <- ut.findScanMailbox(id, UserTaskScope(user.account)) - res <- OptionT.liftF(taskToSettings(user.account, backend, task)) + res <- OptionT.liftF(taskToSettings(user.account.userId, backend, task)) resp <- OptionT.liftF(Ok(res)) } yield resp).getOrElseF(NotFound()) @@ -102,7 +102,7 @@ object ScanMailboxRoutes { def makeTask[F[_]: Sync]( id: Ident, - user: AccountId, + user: AccountInfo, settings: ScanMailboxSettings ): F[UserTask[ScanMailboxArgs]] = Sync[F].pure( @@ -133,14 +133,14 @@ object ScanMailboxRoutes { ) def taskToSettings[F[_]: Sync]( - account: AccountId, + userId: Ident, backend: BackendApp[F], task: UserTask[ScanMailboxArgs] ): F[ScanMailboxSettings] = for { conn <- backend.mail - .getImapSettings(account, None) + .getImapSettings(userId, None) .map( _.find(_.name == task.args.imapConnection) .map(_.name) diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/SentMailRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/SentMailRoutes.scala index 16045709..9b016e28 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/SentMailRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/SentMailRoutes.scala @@ -30,19 +30,19 @@ object SentMailRoutes { HttpRoutes.of { case GET -> Root / "item" / Ident(id) => for { - all <- backend.mail.getSentMailsForItem(user.account, id) + all <- backend.mail.getSentMailsForItem(user.account.collectiveId, id) resp <- Ok(SentMails(all.map(convert).toList)) } yield resp case GET -> Root / "mail" / Ident(mailId) => (for { - mail <- backend.mail.getSentMail(user.account, mailId) + mail <- backend.mail.getSentMail(user.account.collectiveId, mailId) resp <- OptionT.liftF(Ok(convert(mail))) } yield resp).getOrElseF(NotFound()) case DELETE -> Root / "mail" / Ident(mailId) => for { - n <- backend.mail.deleteSentMail(user.account, mailId) + n <- backend.mail.deleteSentMail(user.account.collectiveId, mailId) resp <- Ok(BasicResult(n > 0, s"Mails deleted: $n")) } yield resp } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ShareRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ShareRoutes.scala index 2b16263b..a8fd11da 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ShareRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ShareRoutes.scala @@ -35,9 +35,9 @@ object ShareRoutes { HttpRoutes.of { case GET -> Root :? QP.Query(q) :? QP.OwningFlag(owning) => - val login = if (owning) Some(user.account.user) else None + val login = if (owning) Some(user.account.login) else None for { - all <- backend.share.findAll(user.account.collective, login, q) + all <- backend.share.findAll(user.account.collectiveId, login, q) now <- Timestamp.current[F] res <- Ok(ShareList(all.map(mkShareDetail(now)))) } yield res @@ -52,7 +52,7 @@ object ShareRoutes { case GET -> Root / Ident(id) => (for { - share <- backend.share.findOne(id, user.account.collective) + share <- backend.share.findOne(id, user.account.collectiveId) now <- OptionT.liftF(Timestamp.current[F]) resp <- OptionT.liftF(Ok(mkShareDetail(now)(share))) } yield resp).getOrElseF(NotFound()) @@ -67,7 +67,7 @@ object ShareRoutes { case DELETE -> Root / Ident(id) => for { - del <- backend.share.delete(id, user.account.collective) + del <- backend.share.delete(id, user.account.collectiveId) resp <- Ok(BasicResult(del, if (del) "Share deleted." else "Deleting failed.")) } yield resp @@ -75,7 +75,10 @@ object ShareRoutes { for { in <- req.as[SimpleShareMail] mail = convertIn(in) - res <- mail.traverse(m => backend.share.sendMail(user.account, name, m)) + res <- mail.traverse(m => + backend.share + .sendMail(user.account.collectiveId, user.account.userId, name, m) + ) resp <- res.fold( err => Ok(BasicResult(false, s"Invalid mail data: $err")), res => Ok(convertOut(res)) @@ -111,7 +114,7 @@ object ShareRoutes { def mkNewShare(data: ShareData, user: AuthToken): OShare.NewShare = OShare.NewShare( - user.account, + user.account.asAccountId, data.name, data.query, data.enabled, @@ -159,7 +162,7 @@ object ShareRoutes { ShareDetail( r.share.id, r.share.query, - IdName(r.user.uid, r.user.login.id), + IdName(r.account.userId, r.account.login.id), r.share.name, r.share.enabled, r.share.publishAt, diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/SourceRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/SourceRoutes.scala index f67fd772..9e1a621a 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/SourceRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/SourceRoutes.scala @@ -30,14 +30,14 @@ object SourceRoutes { HttpRoutes.of { case GET -> Root => for { - all <- backend.source.findAll(user.account) + all <- backend.source.findAll(user.account.collectiveId) res <- Ok(SourceList(all.map(mkSource).toList)) } yield res case req @ POST -> Root => for { data <- req.as[SourceTagIn] - src <- newSource(data.source, user.account.collective) + src <- newSource(data.source, user.account.collectiveId) added <- backend.source.add(src, data.tags) resp <- Ok(basicResult(added, "Source added.")) } yield resp @@ -45,14 +45,14 @@ object SourceRoutes { case req @ PUT -> Root => for { data <- req.as[SourceTagIn] - src = changeSource(data.source, user.account.collective) + src = changeSource(data.source, user.account.collectiveId) updated <- backend.source.update(src, data.tags) resp <- Ok(basicResult(updated, "Source updated.")) } yield resp case DELETE -> Root / Ident(id) => for { - del <- backend.source.delete(id, user.account.collective) + del <- backend.source.delete(id, user.account.collectiveId) resp <- Ok(basicResult(del, "Source deleted.")) } yield resp } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/TagRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/TagRoutes.scala index e503b034..e42356c7 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/TagRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/TagRoutes.scala @@ -32,7 +32,7 @@ object TagRoutes { case GET -> Root :? QueryParam.QueryOpt(q) :? QueryParam.TagSort(sort) => for { all <- backend.tag.findAll( - user.account, + user.account.collectiveId, q.map(_.q), sort.getOrElse(TagOrder.NameAsc) ) @@ -42,7 +42,7 @@ object TagRoutes { case req @ POST -> Root => for { data <- req.as[Tag] - tag <- newTag(data, user.account.collective) + tag <- newTag(data, user.account.collectiveId) res <- backend.tag.add(tag) resp <- Ok(basicResult(res, "Tag successfully created.")) } yield resp @@ -50,14 +50,14 @@ object TagRoutes { case req @ PUT -> Root => for { data <- req.as[Tag] - tag = changeTag(data, user.account.collective) + tag = changeTag(data, user.account.collectiveId) res <- backend.tag.update(tag) resp <- Ok(basicResult(res, "Tag successfully updated.")) } yield resp case DELETE -> Root / Ident(id) => for { - del <- backend.tag.delete(id, user.account.collective) + del <- backend.tag.delete(id, user.account.collectiveId) resp <- Ok(basicResult(del, "Tag successfully deleted.")) } yield resp } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/UploadRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/UploadRoutes.scala index 3fe1c379..20bd3484 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/UploadRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/UploadRoutes.scala @@ -33,7 +33,13 @@ object UploadRoutes { val dsl = new Http4sDsl[F] with ResponseGenerator[F] {} import dsl._ - val submitting = submitFiles[F](backend, cfg, Right(user.account)) _ + val submitting = + submitFiles[F]( + backend, + cfg, + Right(user.account.collectiveId), + user.account.userId.some + ) _ HttpRoutes.of { case req @ POST -> Root / "item" => @@ -53,7 +59,7 @@ object UploadRoutes { (for { _ <- OptionT(backend.collective.findEnabledSource(srcId)) res <- OptionT.liftF( - submitFiles(backend, cfg, Left(srcId))(req, None, Priority.Low, dsl) + submitFiles(backend, cfg, Left(srcId), None)(req, None, Priority.Low, dsl) ) } yield res).getOrElseF(NotFound()) @@ -61,7 +67,12 @@ object UploadRoutes { (for { _ <- OptionT(backend.collective.findEnabledSource(srcId)) res <- OptionT.liftF( - submitFiles(backend, cfg, Left(srcId))(req, Some(itemId), Priority.Low, dsl) + submitFiles(backend, cfg, Left(srcId), None)( + req, + Some(itemId), + Priority.Low, + dsl + ) ) } yield res).getOrElseF(NotFound()) } @@ -70,7 +81,8 @@ object UploadRoutes { private def submitFiles[F[_]: Async]( backend: BackendApp[F], cfg: Config, - accOrSrc: Either[Ident, AccountId] + accOrSrc: Either[Ident, CollectiveId], + userId: Option[Ident] )( req: Request[F], itemId: Option[Ident], @@ -96,7 +108,7 @@ object UploadRoutes { prio, cfg.backend.files.validMimeTypes ) - result <- backend.upload.submitEither(updata, accOrSrc, itemId) + result <- backend.upload.submitEither(updata, accOrSrc, userId, itemId) res <- Ok(basicResult(result)) } yield res } diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/UserRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/UserRoutes.scala index 591c124b..784a423a 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/UserRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/UserRoutes.scala @@ -8,14 +8,13 @@ package docspell.restserver.routes import cats.effect._ import cats.implicits._ - import docspell.backend.BackendApp import docspell.backend.auth.AuthToken import docspell.backend.ops.OCollective import docspell.common._ import docspell.restapi.model._ import docspell.restserver.conv.Conversions._ - +import docspell.store.UpdateResult import org.http4s.HttpRoutes import org.http4s.circe.CirceEntityDecoder._ import org.http4s.circe.CirceEntityEncoder._ @@ -32,7 +31,8 @@ object UserRoutes { for { data <- req.as[PasswordChange] res <- backend.collective.changePassword( - user.account, + user.account.collectiveId, + user.account.userId, data.currentPassword, data.newPassword ) @@ -41,14 +41,14 @@ object UserRoutes { case GET -> Root => for { - all <- backend.collective.listUser(user.account.collective) + all <- backend.collective.listUser(user.account.collectiveId) res <- Ok(UserList(all.map(mkUser).toList)) } yield res case req @ POST -> Root => for { data <- req.as[User] - nuser <- newUser(data, user.account.collective) + nuser <- newUser(data, user.account.collectiveId) added <- backend.collective.add(nuser) resp <- Ok(basicResult(added, "User created.")) } yield resp @@ -56,25 +56,35 @@ object UserRoutes { case req @ PUT -> Root => for { data <- req.as[User] - nuser = changeUser(data, user.account.collective) + nuser = changeUser(data, user.account.collectiveId) update <- backend.collective.update(nuser) resp <- Ok(basicResult(update, "User updated.")) } yield resp case DELETE -> Root / Ident(id) => for { - ar <- backend.collective.deleteUser(id, user.account.collective) + users <- backend.collective.listUser(user.account.collectiveId) + ar <- + if (users.exists(_.uid == id)) backend.collective.deleteUser(id) + else UpdateResult.notFound.pure[F] resp <- Ok(basicResult(ar, "User deleted.")) } yield resp case GET -> Root / Ident(username) / "deleteData" => for { - data <- backend.collective.getDeleteUserData( - AccountId(user.account.collective, username) - ) - resp <- Ok( - DeleteUserData(data.ownedFolders, data.sentMails, data.shares) - ) + users <- backend.collective.listUser(user.account.collectiveId) + userToDelete = users.find(u => u.login == username || u.uid == username) + resp <- userToDelete match { + case Some(user) => + backend.collective + .getDeleteUserData(user.cid, user.uid) + .flatMap(data => + Ok(DeleteUserData(data.ownedFolders, data.sentMails, data.shares)) + ) + + case None => + NotFound(BasicResult(false, s"User '${username.id}' not found")) + } } yield resp } } diff --git a/modules/restserver/src/main/scala/docspell/restserver/ws/WebSocketRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/ws/WebSocketRoutes.scala index febbc0e1..42ba54b2 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/ws/WebSocketRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/ws/WebSocketRoutes.scala @@ -10,10 +10,9 @@ import cats.effect.Async import cats.implicits._ import fs2.concurrent.Topic import fs2.{Pipe, Stream} - import docspell.backend.BackendApp import docspell.backend.auth.AuthToken - +import docspell.scheduler.usertask.UserTaskScope import org.http4s.HttpRoutes import org.http4s.dsl.Http4sDsl import org.http4s.server.websocket.WebSocketBuilder2 @@ -34,7 +33,7 @@ object WebSocketRoutes { HttpRoutes.of { case GET -> Root => val init = for { - jc <- backend.job.getUnfinishedJobCount(user.account.collective) + jc <- backend.job.getUnfinishedJobCount(UserTaskScope(user.account)) msg = OutputEvent.JobsWaiting(user.account.collective, jc) } yield Text(msg.encode) diff --git a/modules/store/src/main/scala/db/migration/MigrationTasks.scala b/modules/store/src/main/scala/db/migration/MigrationTasks.scala index ec237f55..7a803e3c 100644 --- a/modules/store/src/main/scala/db/migration/MigrationTasks.scala +++ b/modules/store/src/main/scala/db/migration/MigrationTasks.scala @@ -16,7 +16,11 @@ import docspell.notification.api._ import docspell.store.queries.QLogin import docspell.store.records._ -import db.migration.data._ +import db.migration.data.{ + PeriodicDueItemsArgs => PeriodicDueItemsArgsLegacy, + PeriodicQueryArgs => PeriodicQueryArgsLegacy, + _ +} import doobie._ import doobie.implicits._ import doobie.util.transactor.Strategy @@ -75,8 +79,8 @@ trait MigrationTasks { ref.flatMap(channelRef => RPeriodicTask.updateTask( old.id, - PeriodicQueryArgs.taskName, - PeriodicQueryArgs( + PeriodicQueryArgsLegacy.taskName, + PeriodicQueryArgsLegacy( oldArgs.account, NonEmptyList.of(channelRef), oldArgs.query, @@ -105,8 +109,8 @@ trait MigrationTasks { ref.flatMap(channelRef => RPeriodicTask.updateTask( old.id, - PeriodicDueItemsArgs.taskName, - PeriodicDueItemsArgs( + PeriodicDueItemsArgsLegacy.taskName, + PeriodicDueItemsArgsLegacy( oldArgs.account, NonEmptyList.of(channelRef), oldArgs.remindDays, @@ -147,7 +151,7 @@ trait MigrationTasks { RPeriodicTask .updateTask( old.id, - PeriodicDueItemsArgs.taskName, + PeriodicDueItemsArgsLegacy.taskName, a.asJson.noSpaces ) ) @@ -163,7 +167,7 @@ trait MigrationTasks { private def convertArgs( old: NotifyDueItemsArgs - ): OptionT[ConnectionIO, PeriodicDueItemsArgs] = { + ): OptionT[ConnectionIO, PeriodicDueItemsArgsLegacy] = { val recs = old.recipients .map(MailAddress.parse) .flatMap { @@ -188,7 +192,7 @@ trait MigrationTasks { now ) _ <- OptionT.liftF(RNotificationChannelMail.insert(ch)) - args = PeriodicDueItemsArgs( + args = PeriodicDueItemsArgsLegacy( old.account, NonEmptyList.of(ChannelRef(ch.id, ChannelType.Mail, chName)), old.remindDays, diff --git a/modules/store/src/main/scala/db/migration/data/PeriodicDueItemsArgs.scala b/modules/store/src/main/scala/db/migration/data/PeriodicDueItemsArgs.scala new file mode 100644 index 00000000..477924d6 --- /dev/null +++ b/modules/store/src/main/scala/db/migration/data/PeriodicDueItemsArgs.scala @@ -0,0 +1,43 @@ +/* + * Copyright 2020 Eike K. & Contributors + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package db.migration.data + +import cats.data.NonEmptyList +import docspell.common._ +import docspell.notification.api.ChannelRef +import io.circe.generic.semiauto +import io.circe.{Decoder, Encoder} + +/** Arguments to the notification task. + * + * This tasks queries items with a due date and informs the user via mail. + * + * If the structure changes, there must be some database migration to update or remove + * the json data of the corresponding task. + * + * @deprecated + * replaced with a version using `AccountInfo` + */ +final case class PeriodicDueItemsArgs( + account: AccountId, + channels: NonEmptyList[ChannelRef], + remindDays: Int, + daysBack: Option[Int], + tagsInclude: List[Ident], + tagsExclude: List[Ident], + baseUrl: Option[LenientUri] +) + +object PeriodicDueItemsArgs { + val taskName = Ident.unsafe("periodic-due-items-notify2") + + implicit val jsonDecoder: Decoder[PeriodicDueItemsArgs] = + semiauto.deriveDecoder + + implicit val jsonEncoder: Encoder[PeriodicDueItemsArgs] = + semiauto.deriveEncoder +} diff --git a/modules/store/src/main/scala/db/migration/data/PeriodicQueryArgs.scala b/modules/store/src/main/scala/db/migration/data/PeriodicQueryArgs.scala new file mode 100644 index 00000000..5ff6d94e --- /dev/null +++ b/modules/store/src/main/scala/db/migration/data/PeriodicQueryArgs.scala @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Eike K. & Contributors + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package db.migration.data + +import cats.data.NonEmptyList +import docspell.common._ +import docspell.notification.api.ChannelRef +import io.circe.generic.semiauto +import io.circe.{Decoder, Encoder} + +/** @deprecated replaced with a version using `AccountInfo` */ +final case class PeriodicQueryArgs( + account: AccountId, + channels: NonEmptyList[ChannelRef], + query: Option[ItemQueryString], + bookmark: Option[String], + baseUrl: Option[LenientUri], + contentStart: Option[String] +) + +object PeriodicQueryArgs { + val taskName = Ident.unsafe("periodic-query-notify2") + + implicit val jsonDecoder: Decoder[PeriodicQueryArgs] = + semiauto.deriveDecoder + + implicit def jsonEncoder: Encoder[PeriodicQueryArgs] = + semiauto.deriveEncoder +} diff --git a/modules/store/src/main/scala/db/migration/data/ScanMailboxArgs.scala b/modules/store/src/main/scala/db/migration/data/ScanMailboxArgs.scala new file mode 100644 index 00000000..64334523 --- /dev/null +++ b/modules/store/src/main/scala/db/migration/data/ScanMailboxArgs.scala @@ -0,0 +1,69 @@ +/* + * Copyright 2020 Eike K. & Contributors + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package db.migration.data + +import docspell.common._ +import docspell.common.syntax.all._ + +import io.circe._ +import io.circe.generic.semiauto._ + +/** Arguments to the poll-mailbox task. + * + * This tasks queries user mailboxes and pushes found mails into docspell for processing. + * + * If the structure changes, there must be some database migration to update or remove + * the json data of the corresponding task. + * + * @deprecated + * replaced with a version using `AccountInfo` + */ +case class ScanMailboxArgs( + // the docspell user account + account: AccountId, + // the configured imap connection + imapConnection: Ident, + // scan folders recursively + scanRecursively: Option[Boolean], + // what folders to search + folders: List[String], + // only select mails received since then + receivedSince: Option[Duration], + // move submitted mails to another folder + targetFolder: Option[String], + // delete the after submitting (only if targetFolder is None) + deleteMail: Boolean, + // set the direction when submitting + direction: Option[Direction], + // set a folder for items + itemFolder: Option[Ident], + // set a filter for files when importing archives + fileFilter: Option[Glob], + // set a list of tags to apply to new item + tags: Option[List[String]], + // a glob filter for the mail subject + subjectFilter: Option[Glob], + // the language for extraction and analysis + language: Option[Language], + // apply additional filter to all mails or only imported + postHandleAll: Option[Boolean], + // Exclude the mail body when importing + attachmentsOnly: Option[Boolean] +) + +object ScanMailboxArgs { + + val taskName = Ident.unsafe("scan-mailbox") + + implicit val jsonEncoder: Encoder[ScanMailboxArgs] = + deriveEncoder[ScanMailboxArgs] + implicit val jsonDecoder: Decoder[ScanMailboxArgs] = + deriveDecoder[ScanMailboxArgs] + + def parse(str: String): Either[Throwable, ScanMailboxArgs] = + str.parseJsonAs[ScanMailboxArgs] +} diff --git a/modules/store/src/main/scala/docspell/store/records/RFolder.scala b/modules/store/src/main/scala/docspell/store/records/RFolder.scala index b4ef19b3..37f9c856 100644 --- a/modules/store/src/main/scala/docspell/store/records/RFolder.scala +++ b/modules/store/src/main/scala/docspell/store/records/RFolder.scala @@ -30,12 +30,12 @@ object RFolder { def newFolder[F[_]: Sync]( name: String, collective: CollectiveId, - user: Ident + ownerUserId: Ident ): F[RFolder] = for { nId <- Ident.randomId[F] now <- Timestamp.current[F] - } yield RFolder(nId, name, collective, user, now) + } yield RFolder(nId, name, collective, ownerUserId, now) final case class Table(alias: Option[String]) extends TableDef { val tableName = "folder"