Merge pull request #2348 from rehanone/update/scala-fs2-despendencies

Updated fs2 and related dependencies
This commit is contained in:
eikek 2023-11-01 07:36:44 +01:00 committed by GitHub
commit c0f684eb0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 224 additions and 150 deletions

View File

@ -16,7 +16,7 @@ val scalafixSettings = Seq(
val sharedSettings = Seq(
organization := "com.github.eikek",
scalaVersion := "2.13.10",
scalaVersion := "2.13.12",
organizationName := "Eike K. & Contributors",
licenses += ("AGPL-3.0-or-later", url(
"https://spdx.org/licenses/AGPL-3.0-or-later.html"

View File

@ -19,7 +19,7 @@ final case class AddonArchive(url: LenientUri, name: String, version: String) {
def nameAndVersion: String =
s"$name-$version"
def extractTo[F[_]: Async](
def extractTo[F[_]: Async: Files](
reader: UrlReader[F],
directory: Path,
withSubdir: Boolean = true,
@ -48,7 +48,7 @@ final case class AddonArchive(url: LenientUri, name: String, version: String) {
/** Read meta either from the given directory or extract the url to find the metadata
* file to read
*/
def readMeta[F[_]: Async](
def readMeta[F[_]: Async: Files](
urlReader: UrlReader[F],
directory: Option[Path] = None
): F[AddonMeta] =
@ -58,7 +58,7 @@ final case class AddonArchive(url: LenientUri, name: String, version: String) {
}
object AddonArchive {
def read[F[_]: Async](
def read[F[_]: Async: Files](
url: LenientUri,
urlReader: UrlReader[F],
extractDir: Option[Path] = None
@ -69,7 +69,7 @@ object AddonArchive {
.map(m => addon.copy(name = m.meta.name, version = m.meta.version))
}
def dockerAndFlakeExists[F[_]: Async](
def dockerAndFlakeExists[F[_]: Async: Files](
archive: Either[Path, Stream[F, Byte]]
): F[(Boolean, Boolean)] = {
val files = Files[F]

View File

@ -29,7 +29,7 @@ trait AddonExecutor[F[_]] {
object AddonExecutor {
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: AddonExecutorConfig,
urlReader: UrlReader[F]
): AddonExecutor[F] =
@ -104,7 +104,7 @@ object AddonExecutor {
} yield result
}
def selectRunner[F[_]: Async](
def selectRunner[F[_]: Async: Files](
cfg: AddonExecutorConfig,
meta: AddonMeta,
addonDir: Path

View File

@ -50,7 +50,7 @@ case class AddonMeta(
* inspecting the archive to return defaults when the addon isn't declaring it in the
* descriptor.
*/
def enabledTypes[F[_]: Async](
def enabledTypes[F[_]: Async: Files](
archive: Either[Path, Stream[F, Byte]]
): F[List[RunnerType]] =
for {
@ -207,7 +207,7 @@ object AddonMeta {
)
}
def findInZip[F[_]: Async](zipFile: Stream[F, Byte]): F[AddonMeta] = {
def findInZip[F[_]: Async: Files](zipFile: Stream[F, Byte]): F[AddonMeta] = {
val logger = docspell.logging.getLogger[F]
val fail: F[AddonMeta] = Async[F].raiseError(
new FileNotFoundException(

View File

@ -10,6 +10,7 @@ import cats.Applicative
import cats.effect._
import cats.syntax.all._
import fs2.Stream
import fs2.io.file.Files
import docspell.addons.runner._
import docspell.common.exec.Env
@ -26,7 +27,9 @@ trait AddonRunner[F[_]] {
}
object AddonRunner {
def forType[F[_]: Async](cfg: AddonExecutorConfig)(rt: RunnerType) =
def forType[F[_]: Async: Files](
cfg: AddonExecutorConfig
)(rt: RunnerType): AddonRunner[F] =
rt match {
case RunnerType.NixFlake => NixFlakeRunner[F](cfg)
case RunnerType.Docker => DockerRunner[F](cfg)
@ -38,9 +41,9 @@ object AddonRunner {
def pure[F[_]: Applicative](result: AddonResult): AddonRunner[F] =
new AddonRunner[F] {
val runnerType = Nil
val runnerType: List[RunnerType] = Nil
def run(logger: Logger[F], env: Env, ctx: Context) =
def run(logger: Logger[F], env: Env, ctx: Context): F[AddonResult] =
Applicative[F].pure(result)
}
@ -50,9 +53,9 @@ object AddonRunner {
case a :: Nil => a
case _ =>
new AddonRunner[F] {
val runnerType = runners.flatMap(_.runnerType).distinct
val runnerType: List[RunnerType] = runners.flatMap(_.runnerType).distinct
def run(logger: Logger[F], env: Env, ctx: Context) =
def run(logger: Logger[F], env: Env, ctx: Context): F[AddonResult] =
Stream
.emits(runners)
.evalTap(r =>

View File

@ -18,7 +18,7 @@ import docspell.common.Duration
import docspell.common.exec._
import docspell.logging.Logger
final class NixFlakeRunner[F[_]: Async](cfg: NixFlakeRunner.Config)
final class NixFlakeRunner[F[_]: Async: Files](cfg: NixFlakeRunner.Config)
extends AddonRunner[F] {
val runnerType = List(RunnerType.NixFlake)
@ -104,7 +104,7 @@ final class NixFlakeRunner[F[_]: Async](cfg: NixFlakeRunner.Config)
}
object NixFlakeRunner {
def apply[F[_]: Async](cfg: AddonExecutorConfig): NixFlakeRunner[F] =
def apply[F[_]: Async: Files](cfg: AddonExecutorConfig): NixFlakeRunner[F] =
new NixFlakeRunner[F](Config(cfg.nixRunner, cfg.nspawn, cfg.runTimeout))
case class Config(

View File

@ -45,7 +45,7 @@ private[addons] object RunnerUtil {
* expected to be relative to the `ctx.baseDir`. Additional arguments and environment
* variables are added as configured in the addon.
*/
def runInContainer[F[_]: Async](
def runInContainer[F[_]: Async: Files](
logger: Logger[F],
cfg: AddonExecutorConfig.NSpawn,
ctx: Context

View File

@ -19,10 +19,12 @@ import docspell.common.Duration
import docspell.common.exec.{Args, Env, SysCmd}
import docspell.logging.Logger
final class TrivialRunner[F[_]: Async](cfg: TrivialRunner.Config) extends AddonRunner[F] {
final class TrivialRunner[F[_]: Async: Files](cfg: TrivialRunner.Config)
extends AddonRunner[F] {
private val sync = Async[F]
private val files = Files[F]
implicit val andMonoid: Monoid[Boolean] = Monoid.instance[Boolean](true, _ && _)
implicit val andMonoid: Monoid[Boolean] =
Monoid.instance[Boolean](emptyValue = true, _ && _)
private val executeBits = PosixPermissions(
OwnerExecute,
@ -34,13 +36,13 @@ final class TrivialRunner[F[_]: Async](cfg: TrivialRunner.Config) extends AddonR
OthersRead
)
val runnerType = List(RunnerType.Trivial)
val runnerType: List[RunnerType] = List(RunnerType.Trivial)
def run(
logger: Logger[F],
env: Env,
ctx: Context
) = {
): F[AddonResult] = {
val binaryPath = ctx.meta.runner
.flatMap(_.trivial)
.map(_.exec)
@ -71,7 +73,7 @@ final class TrivialRunner[F[_]: Async](cfg: TrivialRunner.Config) extends AddonR
}
object TrivialRunner {
def apply[F[_]: Async](cfg: AddonExecutorConfig): TrivialRunner[F] =
def apply[F[_]: Async: Files](cfg: AddonExecutorConfig): TrivialRunner[F] =
new TrivialRunner[F](Config(cfg.nspawn, cfg.runTimeout))
case class Config(nspawn: NSpawn, timeout: Duration)

View File

@ -9,6 +9,7 @@ package docspell.analysis
import cats.Applicative
import cats.effect._
import cats.implicits._
import fs2.io.file.Files
import docspell.analysis.classifier.{StanfordTextClassifier, TextClassifier}
import docspell.analysis.contact.Contact
@ -36,7 +37,7 @@ object TextAnalyser {
labels ++ dates.map(dl => dl.label.copy(label = dl.date.toString))
}
def create[F[_]: Async](cfg: TextAnalysisConfig): Resource[F, TextAnalyser[F]] =
def create[F[_]: Async: Files](cfg: TextAnalysisConfig): Resource[F, TextAnalyser[F]] =
Resource
.eval(Nlp(cfg.nlpConfig))
.map(stanfordNer =>
@ -83,7 +84,7 @@ object TextAnalyser {
/** Provides the nlp pipeline based on the configuration. */
private object Nlp {
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: TextAnalysisConfig.NlpConfig
): F[Input[F] => F[Vector[NerLabel]]] = {
val log = docspell.logging.getLogger[F]

View File

@ -21,7 +21,7 @@ import docspell.logging.Logger
import edu.stanford.nlp.classify.ColumnDataClassifier
final class StanfordTextClassifier[F[_]: Async](cfg: TextClassifierConfig)
final class StanfordTextClassifier[F[_]: Async: Files](cfg: TextClassifierConfig)
extends TextClassifier[F] {
def trainClassifier[A](

View File

@ -11,6 +11,7 @@ import scala.concurrent.duration.{Duration => _, _}
import cats.effect.Ref
import cats.effect._
import cats.implicits._
import fs2.io.file.Files
import docspell.analysis.NlpSettings
import docspell.common._
@ -32,7 +33,7 @@ trait PipelineCache[F[_]] {
object PipelineCache {
private[this] val logger = docspell.logging.unsafeLogger
def apply[F[_]: Async](clearInterval: Duration)(
def apply[F[_]: Async: Files](clearInterval: Duration)(
creator: NlpSettings => Annotator[F],
release: F[Unit]
): F[PipelineCache[F]] = {
@ -44,7 +45,7 @@ object PipelineCache {
} yield new Impl[F](data, creator, cacheClear)
}
final private class Impl[F[_]: Async](
final private class Impl[F[_]: Async: Files](
data: Ref[F, Map[String, Entry[Annotator[F]]]],
creator: NlpSettings => Annotator[F],
cacheClear: CacheClearing[F]

View File

@ -7,6 +7,7 @@
package docspell.backend
import cats.effect._
import fs2.io.file.Files
import docspell.backend.BackendCommands.EventContext
import docspell.backend.auth.Login
@ -65,7 +66,7 @@ trait BackendApp[F[_]] {
object BackendApp {
def create[F[_]: Async](
def create[F[_]: Async: Files](
cfg: Config,
store: Store[F],
javaEmil: Emil[F],

View File

@ -9,6 +9,7 @@ package docspell.backend.joex
import cats.data.OptionT
import cats.effect._
import cats.syntax.all._
import fs2.io.file.Files
import docspell.addons._
import docspell.backend.joex.AddonOps.{AddonRunConfigRef, ExecResult}
@ -98,7 +99,7 @@ object AddonOps {
)
}
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: AddonEnvConfig,
store: Store[F],
cmdRunner: BackendCommandRunner[F, Unit],
@ -160,7 +161,10 @@ object AddonOps {
execRes = ExecResult(List(result), List(runCfg))
} yield execRes
def createMiddleware(custom: Middleware[F], runCfg: AddonRunConfigRef) = for {
def createMiddleware(
custom: Middleware[F],
runCfg: AddonRunConfigRef
): F[Middleware[F]] = for {
dscMW <- prepare.createDscEnv(runCfg, cfg.executorConfig.runTimeout)
mm = dscMW >> custom >> prepare.logResult(logger, runCfg) >> Middleware
.ephemeralRun[F]

View File

@ -10,7 +10,7 @@ import cats.data.EitherT
import cats.effect._
import cats.syntax.all._
import fs2.Stream
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import docspell.addons.{AddonMeta, RunnerType}
import docspell.backend.Config
@ -21,7 +21,7 @@ import docspell.joexapi.model.AddonSupport
import docspell.store.Store
import docspell.store.records.RAddonArchive
final class AddonValidate[F[_]: Async](
final class AddonValidate[F[_]: Async: Files](
cfg: Config.Addons,
store: Store[F],
joexOps: OJoex[F]

View File

@ -9,6 +9,7 @@ package docspell.backend.ops
import cats.data.{EitherT, NonEmptyList, OptionT}
import cats.effect._
import cats.syntax.all._
import fs2.io.file.Files
import docspell.addons.{AddonMeta, AddonTriggerType}
import docspell.backend.ops.AddonValidationError._
@ -129,7 +130,7 @@ object OAddons {
def failure[A](error: AddonValidationError): AddonValidationResult[A] = Left(error)
}
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: Config.Addons,
store: Store[F],
userTasks: UserTaskStore[F],

View File

@ -39,7 +39,7 @@ final case class Binary[F[_]](name: String, mime: MimeType, data: Stream[F, Byte
object Binary {
def apply[F[_]: Async](file: Path): Binary[F] =
def apply[F[_]: Files](file: Path): Binary[F] =
Binary(file.fileName.toString, Files[F].readAll(file))
def apply[F[_]](name: String, data: Stream[F, Byte]): Binary[F] =
@ -74,11 +74,11 @@ object Binary {
data.chunks.map(_.toByteVector).compile.fold(ByteVector.empty)((r, e) => r ++ e)
/** Convert paths into `Binary`s */
def toBinary[F[_]: Async]: Pipe[F, Path, Binary[F]] =
def toBinary[F[_]: Files]: Pipe[F, Path, Binary[F]] =
_.map(Binary[F](_))
/** Save one or more binaries to a target directory. */
def saveTo[F[_]: Async](
def saveTo[F[_]: Async: Files](
logger: Logger[F],
targetDir: Path
): Pipe[F, Binary[F], Path] =

View File

@ -72,6 +72,6 @@ object File {
.drain
.map(_ => file)
def readJson[F[_]: Async, A](file: Path)(implicit d: Decoder[A]): F[A] =
def readJson[F[_]: Async: Files, A](file: Path)(implicit d: Decoder[A]): F[A] =
readText[F](file).map(parser.decode[A]).rethrow
}

View File

@ -7,7 +7,7 @@
package docspell.common.util
import cats.effect._
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import fs2.{Pipe, Stream}
import docspell.common.Glob
@ -33,9 +33,9 @@ trait Zip[F[_]] {
}
object Zip {
val defaultChunkSize = 64 * 1024
private val defaultChunkSize = 64 * 1024
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
logger: Option[Logger[F]] = None,
tempDir: Option[Path] = None
): Zip[F] =

View File

@ -22,7 +22,7 @@ import fs2.{Chunk, Pipe, Stream}
import docspell.common.Glob
import docspell.logging.Logger
final private class ZipImpl[F[_]: Async](
final private class ZipImpl[F[_]: Async: Files](
log: Option[Logger[F]],
tempDir: Option[Path]
) extends Zip[F] {

View File

@ -27,7 +27,10 @@ object ConfigFactory {
* 1. if no file is found, read the config from environment variables falling back to
* the default config
*/
def default[F[_]: Async, C: ClassTag: ConfigReader](logger: Logger[F], atPath: String)(
def default[F[_]: Async: Files, C: ClassTag: ConfigReader](
logger: Logger[F],
atPath: String
)(
args: List[String],
validation: Validation[C]
): F[C] =
@ -74,7 +77,7 @@ object ConfigFactory {
/** Uses the first argument as a path to the config file. If it is specified but the
* file doesn't exist, an exception is thrown.
*/
private def findFileFromArgs[F[_]: Async](args: List[String]): F[Option[Path]] =
private def findFileFromArgs[F[_]: Async: Files](args: List[String]): F[Option[Path]] =
args.headOption
.map(Path.apply)
.traverse(p =>
@ -89,7 +92,7 @@ object ConfigFactory {
* to giving the file as argument, it is not an error to specify a non-existing file
* via a system property.
*/
private def checkSystemProperty[F[_]: Async]: OptionT[F, Path] =
private def checkSystemProperty[F[_]: Async: Files]: OptionT[F, Path] =
for {
cf <- OptionT(
Sync[F].delay(

View File

@ -11,6 +11,7 @@ import java.nio.charset.StandardCharsets
import cats.effect._
import cats.implicits._
import fs2._
import fs2.io.file.Files
import docspell.common._
import docspell.convert.ConversionResult.Handler
@ -32,7 +33,7 @@ trait Conversion[F[_]] {
object Conversion {
def create[F[_]: Async](
def create[F[_]: Async: Files](
cfg: ConvertConfig,
sanitizeHtml: SanitizeHtml,
additionalPasswords: List[Password],

View File

@ -19,7 +19,7 @@ import docspell.logging.Logger
private[extern] object ExternConv {
def toPDF[F[_]: Async, A](
def toPDF[F[_]: Async: Files, A](
name: String,
cmdCfg: SystemCommand.Config,
wd: Path,
@ -71,7 +71,7 @@ private[extern] object ExternConv {
handler.run(ConversionResult.failure(ex))
}
def readResult[F[_]: Async](
def readResult[F[_]: Async: Files](
chunkSize: Int,
logger: Logger[F]
)(out: Path, result: SystemCommand.Result): F[ConversionResult[F]] =
@ -99,7 +99,7 @@ private[extern] object ExternConv {
.pure[F]
}
def readResultTesseract[F[_]: Async](
def readResultTesseract[F[_]: Async: Files](
outPrefix: String,
chunkSize: Int,
logger: Logger[F]
@ -127,7 +127,7 @@ private[extern] object ExternConv {
}
}
private def storeDataToFile[F[_]: Async](
private def storeDataToFile[F[_]: Async: Files](
name: String,
logger: Logger[F],
inFile: Path
@ -146,7 +146,7 @@ private[extern] object ExternConv {
logger.debug(s"$name stdout: ${result.stdout}") *>
logger.debug(s"$name stderr: ${result.stderr}")
private def storeFile[F[_]: Async](
private def storeFile[F[_]: Async: Files](
in: Stream[F, Byte],
target: Path
): F[Unit] =

View File

@ -8,7 +8,7 @@ package docspell.convert.extern
import cats.effect._
import fs2.Stream
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import docspell.common._
import docspell.convert.ConversionResult
@ -17,7 +17,7 @@ import docspell.logging.Logger
object OcrMyPdf {
def toPDF[F[_]: Async, A](
def toPDF[F[_]: Async: Files, A](
cfg: OcrMyPdfConfig,
lang: Language,
chunkSize: Int,

View File

@ -8,7 +8,7 @@ package docspell.convert.extern
import cats.effect._
import fs2.Stream
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import docspell.common._
import docspell.convert.ConversionResult
@ -17,7 +17,7 @@ import docspell.logging.Logger
object Tesseract {
def toPDF[F[_]: Async, A](
def toPDF[F[_]: Async: Files, A](
cfg: TesseractConfig,
lang: Language,
chunkSize: Int,

View File

@ -8,7 +8,7 @@ package docspell.convert.extern
import cats.effect._
import fs2.Stream
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import docspell.common._
import docspell.convert.ConversionResult
@ -17,7 +17,7 @@ import docspell.logging.Logger
object Unoconv {
def toPDF[F[_]: Async, A](
def toPDF[F[_]: Async: Files, A](
cfg: UnoconvConfig,
chunkSize: Int,
logger: Logger[F]

View File

@ -10,7 +10,7 @@ import java.nio.charset.Charset
import cats.effect._
import cats.implicits._
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import fs2.{Chunk, Stream}
import docspell.common._
@ -20,7 +20,7 @@ import docspell.logging.Logger
object Weasyprint {
def toPDF[F[_]: Async, A](
def toPDF[F[_]: Async: Files, A](
cfg: WeasyprintConfig,
chunkSize: Int,
charset: Charset,
@ -46,7 +46,7 @@ object Weasyprint {
)
ExternConv
.toPDF[F, A]("weasyprint", cmdCfg, cfg.workingDir, true, logger, reader)(
.toPDF[F, A]("weasyprint", cmdCfg, cfg.workingDir, useStdin = true, logger, reader)(
inSane,
handler
)

View File

@ -10,7 +10,7 @@ import java.nio.charset.Charset
import cats.effect._
import cats.implicits._
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import fs2.{Chunk, Stream}
import docspell.common._
@ -20,7 +20,7 @@ import docspell.logging.Logger
object WkHtmlPdf {
def toPDF[F[_]: Async, A](
def toPDF[F[_]: Async: Files, A](
cfg: WkHtmlPdfConfig,
chunkSize: Int,
charset: Charset,
@ -46,7 +46,14 @@ object WkHtmlPdf {
)
ExternConv
.toPDF[F, A]("wkhtmltopdf", cmdCfg, cfg.workingDir, true, logger, reader)(
.toPDF[F, A](
"wkhtmltopdf",
cmdCfg,
cfg.workingDir,
useStdin = true,
logger,
reader
)(
inSane,
handler
)

View File

@ -9,6 +9,7 @@ package docspell.extract
import cats.effect._
import cats.implicits._
import fs2.Stream
import fs2.io.file.Files
import docspell.common._
import docspell.extract.internal.Text
@ -32,7 +33,7 @@ trait Extraction[F[_]] {
object Extraction {
def create[F[_]: Async](
def create[F[_]: Async: Files](
logger: Logger[F],
cfg: ExtractConfig
): Extraction[F] =

View File

@ -9,6 +9,7 @@ package docspell.extract
import cats.effect._
import cats.implicits._
import fs2.Stream
import fs2.io.file.Files
import docspell.common.Language
import docspell.extract.internal.Text
@ -24,7 +25,7 @@ object PdfExtract {
Result(t._1, t._2)
}
def get[F[_]: Async](
def get[F[_]: Async: Files](
in: Stream[F, Byte],
lang: Language,
stripMinLen: Int,

View File

@ -8,7 +8,7 @@ package docspell.extract.ocr
import cats.effect._
import fs2.Stream
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import docspell.common._
import docspell.common.util.File
@ -17,7 +17,7 @@ import docspell.logging.Logger
object Ocr {
/** Extract the text of all pages in the given pdf file. */
def extractPdf[F[_]: Async](
def extractPdf[F[_]: Async: Files](
pdf: Stream[F, Byte],
logger: Logger[F],
lang: String,
@ -40,7 +40,7 @@ object Ocr {
): Stream[F, String] =
runTesseractStdin(img, logger, lang, config)
def extractPdFFile[F[_]: Async](
def extractPdFFile[F[_]: Async: Files](
pdf: Path,
logger: Logger[F],
lang: String,
@ -65,7 +65,7 @@ object Ocr {
/** Run ghostscript to extract all pdf pages into tiff files. The files are stored to a
* temporary location on disk and returned.
*/
private[extract] def runGhostscript[F[_]: Async](
private[extract] def runGhostscript[F[_]: Async: Files](
pdf: Stream[F, Byte],
cfg: OcrConfig,
wd: Path,
@ -91,7 +91,7 @@ object Ocr {
/** Run ghostscript to extract all pdf pages into tiff files. The files are stored to a
* temporary location on disk and returned.
*/
private[extract] def runGhostscriptFile[F[_]: Async](
private[extract] def runGhostscriptFile[F[_]: Async: Files](
pdf: Path,
ghostscript: SystemCommand.Config,
wd: Path,

View File

@ -8,6 +8,7 @@ package docspell.extract.ocr
import cats.effect._
import fs2.Stream
import fs2.io.file.Files
import docspell.common._
import docspell.extract.internal.Text
@ -16,7 +17,7 @@ import docspell.logging.Logger
object TextExtract {
def extract[F[_]: Async](
def extract[F[_]: Async: Files](
in: Stream[F, Byte],
logger: Logger[F],
lang: String,
@ -24,7 +25,7 @@ object TextExtract {
): Stream[F, Text] =
extractOCR(in, logger, lang, config)
def extractOCR[F[_]: Async](
def extractOCR[F[_]: Async: Files](
in: Stream[F, Byte],
logger: Logger[F],
lang: String,

View File

@ -7,7 +7,7 @@
package docspell.files
import cats.data.OptionT
import cats.effect.{Async, Sync}
import cats.effect.Sync
import cats.syntax.all._
import fs2.Pipe
import fs2.io.file.{Files, Path}
@ -39,7 +39,7 @@ trait FileSupport {
TikaMimetype.detect[F](bin.data, hint).map(mt => bin.copy(mime = mt))
}
def toBinaryWithMime[F[_]: Async]: Pipe[F, Path, Binary[F]] =
def toBinaryWithMime[F[_]: Sync: Files]: Pipe[F, Path, Binary[F]] =
_.evalMap(file => file.mimeType.map(mt => Binary(file).copy(mime = mt)))
}

View File

@ -7,6 +7,7 @@
package docspell.joex
import cats.effect.Async
import fs2.io.file.Files
import docspell.config.Implicits._
import docspell.config.{ConfigFactory, FtsType, Validation}
@ -25,7 +26,7 @@ object ConfigFile {
// IntelliJ is wrong, this is required
import Implicits._
def loadConfig[F[_]: Async](args: List[String]): F[Config] = {
def loadConfig[F[_]: Async: Files](args: List[String]): F[Config] = {
val logger = docspell.logging.getLogger[F]
ConfigFactory
.default[F, Config](logger, "docspell.joex")(args, validate)

View File

@ -9,6 +9,8 @@ package docspell.joex
import cats.effect._
import cats.implicits._
import fs2.concurrent.SignallingRef
import fs2.io.file.Files
import fs2.io.net.Network
import docspell.backend.MailAddressCodec
import docspell.backend.joex.FindJobOwnerAccount
@ -45,7 +47,7 @@ final class JoexAppImpl[F[_]: Async](
def init: F[Unit] = {
val run = scheduler.start.compile.drain
val prun = periodicScheduler.start.compile.drain
val eventConsume = notificationMod.consumeAllEvents(2).compile.drain
val eventConsume = notificationMod.consumeAllEvents(maxConcurrent = 2).compile.drain
for {
_ <- scheduleBackgroundTasks
_ <- Async[F].start(run)
@ -62,7 +64,9 @@ final class JoexAppImpl[F[_]: Async](
store.transact(RJobLog.findLogs(jobId))
def initShutdown: F[Unit] =
periodicScheduler.shutdown *> scheduler.shutdown(false) *> termSignal.set(true)
periodicScheduler.shutdown *> scheduler.shutdown(cancelAll = false) *> termSignal.set(
true
)
private def scheduleBackgroundTasks: F[Unit] =
HouseKeepingTask
@ -81,7 +85,8 @@ final class JoexAppImpl[F[_]: Async](
private def scheduleEmptyTrashTasks: F[Unit] =
store
.transact(
REmptyTrashSetting.findForAllCollectives(OCollective.EmptyTrash.default, 50)
REmptyTrashSetting
.findForAllCollectives(OCollective.EmptyTrash.default, chunkSize = 50)
)
.evalMap { es =>
val args = EmptyTrashArgs(es.cid, es.minAge)
@ -98,7 +103,7 @@ final class JoexAppImpl[F[_]: Async](
object JoexAppImpl extends MailAddressCodec {
def create[F[_]: Async](
def create[F[_]: Async: Files: Network](
cfg: Config,
termSignal: SignallingRef[F, Boolean],
store: Store[F],
@ -107,12 +112,14 @@ object JoexAppImpl extends MailAddressCodec {
pools: Pools
): Resource[F, JoexApp[F]] =
for {
joexLogger <- Resource.pure(docspell.logging.getLogger[F](s"joex-${cfg.appId.id}"))
joexLogger <- Resource.pure(
docspell.logging.getLogger[F](name = s"joex-${cfg.appId.id}")
)
pubSubT = PubSubT(pubSub, joexLogger)
javaEmil =
JavaMailEmil(Settings.defaultSettings.copy(debug = cfg.mailDebug))
notificationMod <- Resource.eval(
NotificationModuleImpl[F](store, javaEmil, httpClient, 200)
NotificationModuleImpl[F](store, javaEmil, httpClient, queueSize = 200)
)
jobStoreModule = JobStoreModuleBuilder(store)

View File

@ -9,6 +9,8 @@ package docspell.joex
import cats.effect._
import fs2.Stream
import fs2.concurrent.SignallingRef
import fs2.io.file.Files
import fs2.io.net.Network
import docspell.backend.msg.Topics
import docspell.common.Pools
@ -32,7 +34,10 @@ object JoexServer {
exitRef: Ref[F, ExitCode]
)
def stream[F[_]: Async](cfg: Config, pools: Pools): Stream[F, Nothing] = {
def stream[F[_]: Async: Files: Network](
cfg: Config,
pools: Pools
): Stream[F, Nothing] = {
val app = for {
signal <- Resource.eval(SignallingRef[F, Boolean](false))

View File

@ -7,6 +7,8 @@
package docspell.joex
import cats.effect.{Async, Resource}
import fs2.io.file.Files
import fs2.io.net.Network
import docspell.analysis.TextAnalyser
import docspell.backend.BackendCommands
@ -46,7 +48,7 @@ import docspell.store.Store
import emil.Emil
import org.http4s.client.Client
final class JoexTasks[F[_]: Async](
final class JoexTasks[F[_]: Async: Files: Network](
cfg: Config,
store: Store[F],
itemOps: OItem[F],
@ -257,7 +259,7 @@ final class JoexTasks[F[_]: Async](
object JoexTasks {
def resource[F[_]: Async](
def resource[F[_]: Async: Files: Network](
cfg: Config,
pools: Pools,
jobStoreModule: JobStoreModuleBuilder.Module[F],

View File

@ -43,7 +43,7 @@ object GenericItemAddonTask extends LoggerExtension {
"ITEM_PDF_JSON" -> pdfMetaJson
)
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
ops: AddonOps[F],
store: Store[F],
trigger: AddonTriggerType,
@ -57,7 +57,7 @@ object GenericItemAddonTask extends LoggerExtension {
data
)
def addonResult[F[_]: Async](
def addonResult[F[_]: Async: Files](
ops: AddonOps[F],
store: Store[F],
trigger: AddonTriggerType,
@ -73,7 +73,7 @@ object GenericItemAddonTask extends LoggerExtension {
)
}
def prepareItemData[F[_]: Async](
def prepareItemData[F[_]: Async: Files](
logger: Logger[F],
store: Store[F],
data: ItemData,

View File

@ -9,6 +9,7 @@ package docspell.joex.addon
import cats.data.OptionT
import cats.effect._
import cats.syntax.all._
import fs2.io.file.Files
import docspell.addons.AddonTriggerType
import docspell.backend.joex.AddonOps
@ -26,7 +27,10 @@ object ItemAddonTask extends AddonTaskExtension {
def onCancel[F[_]]: Task[F, Args, Unit] =
Task.log(_.warn(s"Cancelling ${name.id} task"))
def apply[F[_]: Async](ops: AddonOps[F], store: Store[F]): Task[F, Args, Result] =
def apply[F[_]: Async: Files](
ops: AddonOps[F],
store: Store[F]
): Task[F, Args, Result] =
Task { ctx =>
(for {
item <- OptionT(

View File

@ -8,7 +8,7 @@ package docspell.joex.analysis
import cats.effect._
import cats.implicits._
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import docspell.analysis.split.TextSplitter
import docspell.common._
@ -39,7 +39,7 @@ object NerFile {
private def jsonFilePath(directory: Path, collective: CollectiveId): Path =
directory.resolve(s"${collective.value}.json")
def find[F[_]: Async](
def find[F[_]: Async: Files](
collective: CollectiveId,
directory: Path
): F[Option[NerFile]] = {

View File

@ -9,7 +9,7 @@ package docspell.joex.analysis
import cats.effect._
import cats.effect.std.Semaphore
import cats.implicits._
import fs2.io.file.Path
import fs2.io.file.{Files, Path}
import docspell.common._
import docspell.common.util.File
@ -32,7 +32,7 @@ object RegexNerFile {
case class Config(maxEntries: Int, directory: Path, minTime: Duration)
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: Config,
store: Store[F]
): Resource[F, RegexNerFile[F]] =
@ -41,7 +41,7 @@ object RegexNerFile {
writer <- Resource.eval(Semaphore(1))
} yield new Impl[F](cfg.copy(directory = dir), store, writer)
final private class Impl[F[_]: Async](
final private class Impl[F[_]: Async: Files](
cfg: Config,
store: Store[F],
writer: Semaphore[F] // TODO allow parallelism per collective

View File

@ -10,6 +10,7 @@ import java.time.format.DateTimeFormatter
import cats.effect._
import cats.syntax.all._
import fs2.io.file.Files
import fs2.{Pipe, Stream}
import docspell.backend.ops.ODownloadAll
@ -28,7 +29,7 @@ object DownloadZipTask {
def onCancel[F[_]]: Task[F, Args, Unit] =
Task.log(_.warn(s"Cancelling ${DownloadZipArgs.taskName.id} task"))
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
chunkSize: Int,
store: Store[F],
downloadOps: ODownloadAll[F]

View File

@ -8,6 +8,7 @@ package docspell.joex.hk
import cats.effect._
import cats.implicits._
import fs2.io.net.Network
import docspell.common._
import docspell.logging.Logger
@ -19,7 +20,7 @@ import org.http4s.client.Client
import org.http4s.ember.client.EmberClientBuilder
object CheckNodesTask {
def apply[F[_]: Async](
def apply[F[_]: Async: Network](
cfg: HouseKeepingConfig.CheckNodes,
store: Store[F]
): Task[F, Unit, CleanupResult] =

View File

@ -8,6 +8,7 @@ package docspell.joex.hk
import cats.effect._
import cats.implicits._
import fs2.io.net.Network
import docspell.backend.ops.{ODownloadAll, OFileRepository}
import docspell.common._
@ -26,7 +27,7 @@ object HouseKeepingTask {
val taskName: Ident = Ident.unsafe("housekeeping")
def apply[F[_]: Async](
def apply[F[_]: Async: Network](
cfg: Config,
store: Store[F],
fileRepo: OFileRepository[F],

View File

@ -21,7 +21,7 @@ import docspell.store.records.RClassifierModel
object Classify {
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
logger: Logger[F],
workingDir: Path,
store: Store[F],

View File

@ -9,6 +9,7 @@ package docspell.joex.learn
import cats.data.OptionT
import cats.effect._
import cats.implicits._
import fs2.io.file.Files
import docspell.analysis.TextAnalyser
import docspell.backend.ops.OCollective
@ -28,7 +29,7 @@ object LearnClassifierTask {
def onCancel[F[_]]: Task[F, Args, Unit] =
Task.log(_.warn("Cancelling learn-classifier task"))
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: Config.TextAnalysis,
store: Store[F],
analyser: TextAnalyser[F]
@ -37,7 +38,7 @@ object LearnClassifierTask {
.flatMap(_ => learnItemEntities(cfg, store, analyser))
.flatMap(_ => Task(_ => Sync[F].delay(System.gc())))
private def learnItemEntities[F[_]: Async](
private def learnItemEntities[F[_]: Async: Files](
cfg: Config.TextAnalysis,
store: Store[F],
analyser: TextAnalyser[F]
@ -56,7 +57,7 @@ object LearnClassifierTask {
else ().pure[F]
}
private def learnTags[F[_]: Async](
private def learnTags[F[_]: Async: Files](
cfg: Config.TextAnalysis,
store: Store[F],
analyser: TextAnalyser[F]

View File

@ -10,6 +10,7 @@ import cats.data.Kleisli
import cats.effect._
import cats.implicits._
import fs2.Stream
import fs2.io.file.Files
import docspell.analysis.TextAnalyser
import docspell.analysis.classifier.TextClassifier.Data
@ -18,7 +19,7 @@ import docspell.scheduler._
import docspell.store.Store
object LearnItemEntities {
def learnAll[F[_]: Async, A](
def learnAll[F[_]: Async: Files, A](
analyser: TextAnalyser[F],
store: Store[F],
collective: CollectiveId,
@ -32,7 +33,7 @@ object LearnItemEntities {
.flatMap(_ => learnConcPerson(analyser, store, collective, maxItems, maxTextLen))
.flatMap(_ => learnConcEquip(analyser, store, collective, maxItems, maxTextLen))
def learnCorrOrg[F[_]: Async, A](
def learnCorrOrg[F[_]: Async: Files, A](
analyser: TextAnalyser[F],
store: Store[F],
collective: CollectiveId,
@ -44,7 +45,7 @@ object LearnItemEntities {
_ => SelectItems.forCorrOrg(store, collective, maxItems, maxTextLen)
)
def learnCorrPerson[F[_]: Async, A](
def learnCorrPerson[F[_]: Async: Files, A](
analyser: TextAnalyser[F],
store: Store[F],
collective: CollectiveId,
@ -56,7 +57,7 @@ object LearnItemEntities {
_ => SelectItems.forCorrPerson(store, collective, maxItems, maxTextLen)
)
def learnConcPerson[F[_]: Async, A](
def learnConcPerson[F[_]: Async: Files, A](
analyser: TextAnalyser[F],
store: Store[F],
collective: CollectiveId,
@ -68,7 +69,7 @@ object LearnItemEntities {
_ => SelectItems.forConcPerson(store, collective, maxItems, maxTextLen)
)
def learnConcEquip[F[_]: Async, A](
def learnConcEquip[F[_]: Async: Files, A](
analyser: TextAnalyser[F],
store: Store[F],
collective: CollectiveId,
@ -80,7 +81,7 @@ object LearnItemEntities {
_ => SelectItems.forConcEquip(store, collective, maxItems, maxTextLen)
)
private def learn[F[_]: Async, A](
private def learn[F[_]: Async: Files, A](
store: Store[F],
analyser: TextAnalyser[F],
collective: CollectiveId

View File

@ -9,6 +9,7 @@ package docspell.joex.learn
import cats.data.Kleisli
import cats.effect._
import cats.implicits._
import fs2.io.file.Files
import docspell.analysis.TextAnalyser
import docspell.common._
@ -18,7 +19,7 @@ import docspell.store.records.RClassifierSetting
object LearnTags {
def learnTagCategory[F[_]: Async, A](
def learnTagCategory[F[_]: Async: Files, A](
analyser: TextAnalyser[F],
store: Store[F],
collective: CollectiveId,
@ -43,7 +44,10 @@ object LearnTags {
)
}
def learnAllTagCategories[F[_]: Async, A](analyser: TextAnalyser[F], store: Store[F])(
def learnAllTagCategories[F[_]: Async: Files, A](
analyser: TextAnalyser[F],
store: Store[F]
)(
collective: CollectiveId,
maxItems: Int,
maxTextLen: Int

View File

@ -18,7 +18,7 @@ import docspell.store.records.RClassifierModel
object StoreClassifierModel {
def handleModel[F[_]: Async](
def handleModel[F[_]: Async: Files](
store: Store[F],
logger: Logger[F],
collective: CollectiveId,

View File

@ -11,6 +11,7 @@ import cats.data.OptionT
import cats.effect._
import cats.implicits._
import fs2.Stream
import fs2.io.file.Files
import docspell.backend.JobFactory
import docspell.common._
@ -35,7 +36,10 @@ import docspell.store.Store
object MultiUploadArchiveTask {
type Args = ProcessItemArgs
def apply[F[_]: Async](store: Store[F], jobStore: JobStore[F]): Task[F, Args, Result] =
def apply[F[_]: Async: Files](
store: Store[F],
jobStore: JobStore[F]
): Task[F, Args, Result] =
Task { ctx =>
ctx.args.files
.traverse { file =>
@ -104,7 +108,7 @@ object MultiUploadArchiveTask {
.map(_.mimetype.matches(MimeType.zip))
.getOrElse(false)
private def extractZip[F[_]: Async](
private def extractZip[F[_]: Async: Files](
store: Store[F],
args: Args
)(file: ProcessItemArgs.File): Stream[F, ProcessItemArgs] =

View File

@ -11,6 +11,7 @@ import cats.data.OptionT
import cats.effect._
import cats.implicits._
import fs2.Stream
import fs2.io.file.Files
import docspell.common._
import docspell.convert.ConversionResult
@ -35,9 +36,9 @@ object PdfConvTask {
deriveEncoder[Args]
}
val taskName = Ident.unsafe("pdf-files-migration")
val taskName: Ident = Ident.unsafe("pdf-files-migration")
def apply[F[_]: Async](cfg: Config, store: Store[F]): Task[F, Args, Unit] =
def apply[F[_]: Async: Files](cfg: Config, store: Store[F]): Task[F, Args, Unit] =
Task { ctx =>
for {
_ <- ctx.logger.info(s"Converting pdf file ${ctx.args} using ocrmypdf")
@ -89,7 +90,7 @@ object PdfConvTask {
else none.pure[F]
}
def convert[F[_]: Async](
def convert[F[_]: Async: Files](
cfg: Config,
ctx: Context[F, Args],
store: Store[F],

View File

@ -11,6 +11,7 @@ import cats.data.{Kleisli, OptionT}
import cats.effect._
import cats.implicits._
import fs2.Stream
import fs2.io.file.Files
import docspell.common._
import docspell.convert.ConversionResult.Handler
@ -35,7 +36,7 @@ import docspell.store.records._
object ConvertPdf {
type Args = ProcessItemArgs
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: ConvertConfig,
store: Store[F],
item: ItemData
@ -76,7 +77,7 @@ object ConvertPdf {
.map(_.mimetype)
.getOrElse(MimeType.octetStream)
def convertSafe[F[_]: Async](
def convertSafe[F[_]: Async: Files](
cfg: ConvertConfig,
sanitizeHtml: SanitizeHtml,
ctx: Context[F, Args],

View File

@ -14,6 +14,7 @@ import cats.implicits._
import cats.kernel.Monoid
import cats.kernel.Order
import fs2.Stream
import fs2.io.file.Files
import docspell.common._
import docspell.common.util.Zip
@ -35,12 +36,12 @@ import emil.Mail
object ExtractArchive {
type Args = ProcessItemArgs
def apply[F[_]: Async](store: Store[F])(
def apply[F[_]: Async: Files](store: Store[F])(
item: ItemData
): Task[F, Args, ItemData] =
multiPass(store, item, None).map(_._2)
def multiPass[F[_]: Async](
def multiPass[F[_]: Async: Files](
store: Store[F],
item: ItemData,
archive: Option[RAttachmentArchive]
@ -50,7 +51,7 @@ object ExtractArchive {
else multiPass(store, t._2, t._1)
}
def singlePass[F[_]: Async](
def singlePass[F[_]: Async: Files](
store: Store[F],
item: ItemData,
archive: Option[RAttachmentArchive]
@ -91,7 +92,7 @@ object ExtractArchive {
.map(_.mimetype)
.getOrElse(MimeType.octetStream)
def extractSafe[F[_]: Async](
def extractSafe[F[_]: Async: Files](
ctx: Context[F, Args],
store: Store[F],
archive: Option[RAttachmentArchive]
@ -137,7 +138,7 @@ object ExtractArchive {
} yield extracted.copy(files = extracted.files.filter(_.id != ra.id))
}
def extractZip[F[_]: Async](
def extractZip[F[_]: Async: Files](
ctx: Context[F, Args],
store: Store[F],
archive: Option[RAttachmentArchive]

View File

@ -10,6 +10,7 @@ import cats.data.OptionT
import cats.effect._
import cats.implicits._
import fs2.Stream
import fs2.io.file.Files
import docspell.analysis.TextAnalyser
import docspell.backend.joex.AddonOps
@ -36,7 +37,7 @@ object ItemHandler {
}
)
def newItem[F[_]: Async](
def newItem[F[_]: Async: Files](
cfg: Config,
store: Store[F],
itemOps: OItem[F],
@ -82,7 +83,7 @@ object ItemHandler {
def isLastRetry[F[_]]: Task[F, Args, Boolean] =
Task(_.isLastRetry)
def safeProcess[F[_]: Async](
def safeProcess[F[_]: Async: Files](
cfg: Config,
store: Store[F],
itemOps: OItem[F],

View File

@ -8,6 +8,7 @@ package docspell.joex.process
import cats.effect._
import cats.implicits._
import fs2.io.file.Files
import docspell.addons.AddonTriggerType
import docspell.analysis.TextAnalyser
@ -22,7 +23,7 @@ import docspell.store.Store
object ProcessItem {
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: Config,
itemOps: OItem[F],
fts: FtsClient[F],
@ -40,7 +41,7 @@ object ProcessItem {
.flatMap(RemoveEmptyItem(itemOps))
.flatMap(RunAddons(addonOps, store, AddonTriggerType.FinalProcessItem))
def processAttachments[F[_]: Async](
def processAttachments[F[_]: Async: Files](
cfg: Config,
fts: FtsClient[F],
analyser: TextAnalyser[F],
@ -49,7 +50,7 @@ object ProcessItem {
)(item: ItemData): Task[F, ProcessItemArgs, ItemData] =
processAttachments0[F](cfg, fts, analyser, regexNer, store, (30, 60, 90))(item)
def analysisOnly[F[_]: Async](
def analysisOnly[F[_]: Async: Files](
cfg: Config,
analyser: TextAnalyser[F],
regexNer: RegexNerFile[F],
@ -61,7 +62,7 @@ object ProcessItem {
.flatMap(CrossCheckProposals[F](store))
.flatMap(SaveProposals[F](store))
private def processAttachments0[F[_]: Async](
private def processAttachments0[F[_]: Async: Files](
cfg: Config,
fts: FtsClient[F],
analyser: TextAnalyser[F],

View File

@ -9,6 +9,7 @@ package docspell.joex.process
import cats.data.OptionT
import cats.effect._
import cats.implicits._
import fs2.io.file.Files
import docspell.addons.AddonTriggerType
import docspell.analysis.TextAnalyser
@ -30,7 +31,7 @@ import docspell.store.records.RItem
object ReProcessItem {
type Args = ReProcessItemArgs
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: Config,
fts: FtsClient[F],
itemOps: OItem[F],
@ -106,7 +107,7 @@ object ReProcessItem {
)
}
def processFiles[F[_]: Async](
def processFiles[F[_]: Async: Files](
cfg: Config,
fts: FtsClient[F],
itemOps: OItem[F],
@ -162,7 +163,7 @@ object ReProcessItem {
def isLastRetry[F[_]]: Task[F, Args, Boolean] =
Task(_.isLastRetry)
def safeProcess[F[_]: Async](
def safeProcess[F[_]: Async: Files](
cfg: Config,
fts: FtsClient[F],
itemOps: OItem[F],

View File

@ -8,6 +8,7 @@ package docspell.joex.process
import cats.effect._
import cats.syntax.all._
import fs2.io.file.Files
import docspell.addons.AddonTriggerType
import docspell.backend.joex.AddonOps
@ -22,7 +23,7 @@ import docspell.store.Store
object RunAddons {
type Args = ProcessItemArgs
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
ops: AddonOps[F],
store: Store[F],
trigger: AddonTriggerType

View File

@ -9,6 +9,7 @@ package docspell.joex.process
import cats.Traverse
import cats.effect._
import cats.implicits._
import fs2.io.file.Files
import docspell.analysis.classifier.TextClassifier
import docspell.analysis.{NlpSettings, TextAnalyser}
@ -26,7 +27,7 @@ import docspell.store.records.{RAttachmentMeta, RClassifierSetting}
object TextAnalysis {
type Args = ProcessItemArgs
def apply[F[_]: Async](
def apply[F[_]: Async: Files](
cfg: Config.TextAnalysis,
analyser: TextAnalyser[F],
nerFile: RegexNerFile[F],
@ -87,7 +88,7 @@ object TextAnalysis {
} yield (rm.copy(nerlabels = labels.all.toList), AttachmentDates(rm, labels.dates))
}
def predictTags[F[_]: Async](
def predictTags[F[_]: Async: Files](
ctx: Context[F, Args],
store: Store[F],
cfg: Config.TextAnalysis,
@ -107,7 +108,7 @@ object TextAnalysis {
} yield tags.flatten
}
def predictItemEntities[F[_]: Async](
def predictItemEntities[F[_]: Async: Files](
ctx: Context[F, Args],
store: Store[F],
cfg: Config.TextAnalysis,
@ -139,7 +140,7 @@ object TextAnalysis {
.map(MetaProposalList.apply)
}
private def makeClassify[F[_]: Async](
private def makeClassify[F[_]: Async: Files](
ctx: Context[F, Args],
store: Store[F],
cfg: Config.TextAnalysis,

View File

@ -9,6 +9,7 @@ package docspell.joex.process
import cats.data.OptionT
import cats.effect._
import cats.implicits._
import fs2.io.file.Files
import docspell.common._
import docspell.extract.{ExtractConfig, ExtractResult, Extraction}
@ -19,7 +20,7 @@ import docspell.store.records.{RAttachment, RAttachmentMeta, RFileMeta}
object TextExtraction {
def apply[F[_]: Async](cfg: ExtractConfig, fts: FtsClient[F], store: Store[F])(
def apply[F[_]: Async: Files](cfg: ExtractConfig, fts: FtsClient[F], store: Store[F])(
item: ItemData
): Task[F, ProcessItemArgs, ItemData] =
Task { ctx =>
@ -66,7 +67,7 @@ object TextExtraction {
case class Result(am: RAttachmentMeta, td: TextData, tags: List[String] = Nil)
def extractTextIfEmpty[F[_]: Async](
def extractTextIfEmpty[F[_]: Async: Files](
ctx: Context[F, ProcessItemArgs],
store: Store[F],
cfg: ExtractConfig,
@ -100,7 +101,7 @@ object TextExtraction {
}
}
def extractTextToMeta[F[_]: Async](
def extractTextToMeta[F[_]: Async: Files](
ctx: Context[F, _],
store: Store[F],
cfg: ExtractConfig,
@ -143,7 +144,7 @@ object TextExtraction {
.flatMap(mt => extr.extractText(data, DataType(mt), lang))
}
private def extractTextFallback[F[_]: Async](
private def extractTextFallback[F[_]: Async: Files](
ctx: Context[F, _],
store: Store[F],
cfg: ExtractConfig,

View File

@ -8,6 +8,7 @@ package docspell.joexapi.client
import cats.effect._
import cats.implicits._
import fs2.io.net.Network
import docspell.common.{Ident, LenientUri}
import docspell.joexapi.model.{AddonSupport, BasicResult}
@ -72,6 +73,6 @@ object JoexClient {
Uri.unsafeFromString(u.asString)
}
def resource[F[_]: Async]: Resource[F, JoexClient[F]] =
def resource[F[_]: Async: Network]: Resource[F, JoexClient[F]] =
EmberClientBuilder.default[F].build.map(apply[F])
}

View File

@ -10,6 +10,7 @@ import java.security.SecureRandom
import cats.Monoid
import cats.effect.Async
import fs2.io.file.Files
import docspell.backend.auth.Login
import docspell.backend.signup.{Config => SignupConfig}
@ -30,7 +31,7 @@ object ConfigFile {
// IntelliJ is wrong, this is required
import Implicits._
def loadConfig[F[_]: Async](args: List[String]): F[Config] = {
def loadConfig[F[_]: Async: Files](args: List[String]): F[Config] = {
val logger = docspell.logging.getLogger[F]
val validate =
Validation.of(

View File

@ -9,6 +9,7 @@ package docspell.restserver
import cats.effect._
import fs2.Stream
import fs2.concurrent.Topic
import fs2.io.file.Files
import docspell.backend.BackendApp
import docspell.backend.auth.{AuthToken, ShareToken}
@ -36,7 +37,7 @@ import org.http4s.client.Client
import org.http4s.server.Router
import org.http4s.server.websocket.WebSocketBuilder2
final class RestAppImpl[F[_]: Async](
final class RestAppImpl[F[_]: Async: Files](
val config: Config,
val backend: BackendApp[F],
httpClient: Client[F],
@ -162,7 +163,7 @@ final class RestAppImpl[F[_]: Async](
object RestAppImpl {
def create[F[_]: Async](
def create[F[_]: Async: Files](
cfg: Config,
pools: Pools,
store: Store[F],

View File

@ -12,6 +12,8 @@ import cats.effect._
import cats.implicits._
import fs2.Stream
import fs2.concurrent.Topic
import fs2.io.file.Files
import fs2.io.net.Network
import docspell.backend.msg.Topics
import docspell.backend.ops.ONode
@ -35,7 +37,7 @@ import org.http4s.server.websocket.WebSocketBuilder2
object RestServer {
def serve[F[_]: Async](
def serve[F[_]: Async: Files: Network](
cfg: Config,
pools: Pools
): F[ExitCode] =
@ -55,7 +57,7 @@ object RestServer {
.flatMap { case (restApp, pubSub, setting) =>
Stream(
restApp.subscriptions,
restApp.eventConsume(2),
restApp.eventConsume(maxConcurrent = 2),
Stream.resource {
if (cfg.serverOptions.enableHttp2)
EmberServerBuilder
@ -81,7 +83,7 @@ object RestServer {
(server ++ Stream(keepAlive)).parJoinUnbounded.compile.drain.as(ExitCode.Success)
} yield exit
def createApp[F[_]: Async](
def createApp[F[_]: Async: Files: Network](
cfg: Config,
pools: Pools,
wsTopic: Topic[F, OutputEvent]

View File

@ -9,6 +9,7 @@ package docspell.restserver.routes
import cats.data.OptionT
import cats.effect._
import cats.implicits._
import fs2.io.file.Files
import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
@ -25,7 +26,7 @@ import org.log4s._
object UploadRoutes {
private[this] val logger = getLogger
def secured[F[_]: Async](
def secured[F[_]: Async: Files](
backend: BackendApp[F],
cfg: Config,
user: AuthToken
@ -50,7 +51,7 @@ object UploadRoutes {
}
}
def open[F[_]: Async](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
def open[F[_]: Async: Files](backend: BackendApp[F], cfg: Config): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] with ResponseGenerator[F] {}
import dsl._
@ -78,7 +79,7 @@ object UploadRoutes {
}
}
private def submitFiles[F[_]: Async](
private def submitFiles[F[_]: Async: Files](
backend: BackendApp[F],
cfg: Config,
accOrSrc: Either[Ident, CollectiveId],

View File

@ -20,9 +20,9 @@ object Dependencies {
val EmilVersion = "0.13.0"
val FlexmarkVersion = "0.64.8"
val FlywayVersion = "9.22.3"
val Fs2Version = "3.6.1"
val Fs2Version = "3.9.2"
val H2Version = "2.2.224"
val Http4sVersion = "0.23.18"
val Http4sVersion = "0.23.23"
val Icu4jVersion = "74.1"
val JavaOtpVersion = "0.4.0"
val JsoupVersion = "1.16.2"