Apply scalafmt to all files

This commit is contained in:
Eike Kettner
2019-12-30 21:44:13 +01:00
parent 57e274e2b0
commit fc3e22e399
133 changed files with 3003 additions and 2112 deletions

View File

@ -27,38 +27,44 @@ trait BackendApp[F[_]] {
object BackendApp {
def create[F[_]: ConcurrentEffect](cfg: Config, store: Store[F], httpClientEc: ExecutionContext): Resource[F, BackendApp[F]] =
def create[F[_]: ConcurrentEffect](
cfg: Config,
store: Store[F],
httpClientEc: ExecutionContext
): Resource[F, BackendApp[F]] =
for {
queue <- JobQueue(store)
loginImpl <- Login[F](store)
signupImpl <- OSignup[F](store)
collImpl <- OCollective[F](store)
sourceImpl <- OSource[F](store)
tagImpl <- OTag[F](store)
equipImpl <- OEquipment[F](store)
orgImpl <- OOrganization(store)
uploadImpl <- OUpload(store, queue, cfg, httpClientEc)
nodeImpl <- ONode(store)
jobImpl <- OJob(store, httpClientEc)
itemImpl <- OItem(store)
queue <- JobQueue(store)
loginImpl <- Login[F](store)
signupImpl <- OSignup[F](store)
collImpl <- OCollective[F](store)
sourceImpl <- OSource[F](store)
tagImpl <- OTag[F](store)
equipImpl <- OEquipment[F](store)
orgImpl <- OOrganization(store)
uploadImpl <- OUpload(store, queue, cfg, httpClientEc)
nodeImpl <- ONode(store)
jobImpl <- OJob(store, httpClientEc)
itemImpl <- OItem(store)
} yield new BackendApp[F] {
val login: Login[F] = loginImpl
val signup: OSignup[F] = signupImpl
val login: Login[F] = loginImpl
val signup: OSignup[F] = signupImpl
val collective: OCollective[F] = collImpl
val source = sourceImpl
val tag = tagImpl
val equipment = equipImpl
val organization = orgImpl
val upload = uploadImpl
val node = nodeImpl
val job = jobImpl
val item = itemImpl
val source = sourceImpl
val tag = tagImpl
val equipment = equipImpl
val organization = orgImpl
val upload = uploadImpl
val node = nodeImpl
val job = jobImpl
val item = itemImpl
}
def apply[F[_]: ConcurrentEffect: ContextShift](cfg: Config
, connectEC: ExecutionContext
, httpClientEc: ExecutionContext
, blocker: Blocker): Resource[F, BackendApp[F]] =
def apply[F[_]: ConcurrentEffect: ContextShift](
cfg: Config,
connectEC: ExecutionContext,
httpClientEc: ExecutionContext,
blocker: Blocker
): Resource[F, BackendApp[F]] =
for {
store <- Store.create(cfg.jdbc, connectEC, blocker)
backend <- create(cfg, store, httpClientEc)

View File

@ -4,13 +4,9 @@ import docspell.backend.signup.{Config => SignupConfig}
import docspell.common.MimeType
import docspell.store.JdbcConfig
case class Config( jdbc: JdbcConfig
, signup: SignupConfig
, files: Config.Files) {
}
case class Config(jdbc: JdbcConfig, signup: SignupConfig, files: Config.Files) {}
object Config {
case class Files(chunkSize: Int, validMimeTypes: Seq[MimeType])
}
}

View File

@ -50,14 +50,13 @@ object AuthToken {
Left("Invalid authenticator")
}
def user[F[_]: Sync](accountId: AccountId, key: ByteVector): F[AuthToken] = {
def user[F[_]: Sync](accountId: AccountId, key: ByteVector): F[AuthToken] =
for {
salt <- Common.genSaltString[F]
millis = Instant.now.toEpochMilli
cd = AuthToken(millis, accountId, salt, "")
sig = sign(cd, key)
cd = AuthToken(millis, accountId, salt, "")
sig = sign(cd, key)
} yield cd.copy(sig = sig)
}
private def sign(cd: AuthToken, key: ByteVector): String = {
val raw = cd.millis.toString + cd.account.asString + cd.salt

View File

@ -45,45 +45,45 @@ object Login {
}
def ok(session: AuthToken): Result = Ok(session)
def invalidAuth: Result = InvalidAuth
def invalidTime: Result = InvalidTime
def invalidAuth: Result = InvalidAuth
def invalidTime: Result = InvalidTime
}
def apply[F[_]: Effect](store: Store[F]): Resource[F, Login[F]] = Resource.pure(new Login[F] {
def apply[F[_]: Effect](store: Store[F]): Resource[F, Login[F]] =
Resource.pure(new Login[F] {
def loginSession(config: Config)(sessionKey: String): F[Result] =
AuthToken.fromString(sessionKey) match {
case Right(at) =>
if (at.sigInvalid(config.serverSecret)) Result.invalidAuth.pure[F]
else if (at.isExpired(config.sessionValid)) Result.invalidTime.pure[F]
else Result.ok(at).pure[F]
case Left(_) =>
Result.invalidAuth.pure[F]
def loginSession(config: Config)(sessionKey: String): F[Result] =
AuthToken.fromString(sessionKey) match {
case Right(at) =>
if (at.sigInvalid(config.serverSecret)) Result.invalidAuth.pure[F]
else if (at.isExpired(config.sessionValid)) Result.invalidTime.pure[F]
else Result.ok(at).pure[F]
case Left(_) =>
Result.invalidAuth.pure[F]
}
def loginUserPass(config: Config)(up: UserPass): F[Result] =
AccountId.parse(up.user) match {
case Right(acc) =>
val okResult =
store.transact(RUser.updateLogin(acc)) *>
AuthToken.user(acc, config.serverSecret).map(Result.ok)
for {
data <- store.transact(QLogin.findUser(acc))
_ <- Sync[F].delay(logger.trace(s"Account lookup: $data"))
res <- if (data.exists(check(up.pass))) okResult
else Result.invalidAuth.pure[F]
} yield res
case Left(_) =>
Result.invalidAuth.pure[F]
}
private def check(given: String)(data: QLogin.Data): Boolean = {
val collOk = data.collectiveState == CollectiveState.Active ||
data.collectiveState == CollectiveState.ReadOnly
val userOk = data.userState == UserState.Active
val passOk = BCrypt.checkpw(given, data.password.pass)
collOk && userOk && passOk
}
def loginUserPass(config: Config)(up: UserPass): F[Result] = {
AccountId.parse(up.user) match {
case Right(acc) =>
val okResult=
store.transact(RUser.updateLogin(acc)) *>
AuthToken.user(acc, config.serverSecret).map(Result.ok)
for {
data <- store.transact(QLogin.findUser(acc))
_ <- Sync[F].delay(logger.trace(s"Account lookup: $data"))
res <- if (data.exists(check(up.pass))) okResult
else Result.invalidAuth.pure[F]
} yield res
case Left(_) =>
Result.invalidAuth.pure[F]
}
}
private def check(given: String)(data: QLogin.Data): Boolean = {
val collOk = data.collectiveState == CollectiveState.Active ||
data.collectiveState == CollectiveState.ReadOnly
val userOk = data.userState == UserState.Active
val passOk = BCrypt.checkpw(given, data.password.pass)
collOk && userOk && passOk
}
})
})
}

View File

@ -25,7 +25,11 @@ trait OCollective[F[_]] {
def insights(collective: Ident): F[InsightData]
def changePassword(accountId: AccountId, current: Password, newPass: Password): F[PassChangeResult]
def changePassword(
accountId: AccountId,
current: Password,
newPass: Password
): F[PassChangeResult]
}
object OCollective {
@ -35,15 +39,15 @@ object OCollective {
sealed trait PassChangeResult
object PassChangeResult {
case object UserNotFound extends PassChangeResult
case object UserNotFound extends PassChangeResult
case object PasswordMismatch extends PassChangeResult
case object UpdateFailed extends PassChangeResult
case object Success extends PassChangeResult
case object UpdateFailed extends PassChangeResult
case object Success extends PassChangeResult
def userNotFound: PassChangeResult = UserNotFound
def userNotFound: PassChangeResult = UserNotFound
def passwordMismatch: PassChangeResult = PasswordMismatch
def success: PassChangeResult = Success
def updateFailed: PassChangeResult = UpdateFailed
def success: PassChangeResult = Success
def updateFailed: PassChangeResult = UpdateFailed
}
case class RegisterData(collName: Ident, login: Ident, password: Password, invite: Option[Ident])
@ -63,39 +67,47 @@ object OCollective {
}
}
def apply[F[_]:Effect](store: Store[F]): Resource[F, OCollective[F]] =
def apply[F[_]: Effect](store: Store[F]): Resource[F, OCollective[F]] =
Resource.pure(new OCollective[F] {
def find(name: Ident): F[Option[RCollective]] =
store.transact(RCollective.findById(name))
def updateLanguage(collective: Ident, lang: Language): F[AddResult] =
store.transact(RCollective.updateLanguage(collective, lang)).
attempt.map(AddResult.fromUpdate)
store
.transact(RCollective.updateLanguage(collective, lang))
.attempt
.map(AddResult.fromUpdate)
def listUser(collective: Ident): F[Vector[RUser]] = {
def listUser(collective: Ident): F[Vector[RUser]] =
store.transact(RUser.findAll(collective, _.login))
}
def add(s: RUser): F[AddResult] =
store.add(RUser.insert(s.copy(password = PasswordCrypt.crypt(s.password))), RUser.exists(s.login))
store.add(
RUser.insert(s.copy(password = PasswordCrypt.crypt(s.password))),
RUser.exists(s.login)
)
def update(s: RUser): F[AddResult] =
store.add(RUser.update(s), RUser.exists(s.login))
def deleteUser(login: Ident, collective: Ident): F[AddResult] =
store.transact(RUser.delete(login, collective)).
attempt.map(AddResult.fromUpdate)
store.transact(RUser.delete(login, collective)).attempt.map(AddResult.fromUpdate)
def insights(collective: Ident): F[InsightData] =
store.transact(QCollective.getInsights(collective))
def changePassword(accountId: AccountId, current: Password, newPass: Password): F[PassChangeResult] = {
def changePassword(
accountId: AccountId,
current: Password,
newPass: Password
): F[PassChangeResult] = {
val q = for {
optUser <- RUser.findByAccount(accountId)
check = optUser.map(_.password).map(p => PasswordCrypt.check(current, p))
n <- check.filter(identity).traverse(_ => RUser.updatePassword(accountId, PasswordCrypt.crypt(newPass)))
res = check match {
check = optUser.map(_.password).map(p => PasswordCrypt.check(current, p))
n <- check
.filter(identity)
.traverse(_ => RUser.updatePassword(accountId, PasswordCrypt.crypt(newPass)))
res = check match {
case Some(true) =>
if (n.getOrElse(0) > 0) PassChangeResult.success else PassChangeResult.updateFailed
case Some(false) =>

View File

@ -17,7 +17,6 @@ trait OEquipment[F[_]] {
def delete(id: Ident, collective: Ident): F[AddResult]
}
object OEquipment {
def apply[F[_]: Effect](store: Store[F]): Resource[F, OEquipment[F]] =
@ -43,12 +42,10 @@ object OEquipment {
def delete(id: Ident, collective: Ident): F[AddResult] = {
val io = for {
n0 <- RItem.removeConcEquip(collective, id)
n1 <- REquipment.delete(id, collective)
n0 <- RItem.removeConcEquip(collective, id)
n1 <- REquipment.delete(id, collective)
} yield n0 + n1
store.transact(io).
attempt.
map(AddResult.fromUpdate)
store.transact(io).attempt.map(AddResult.fromUpdate)
}
})
}

View File

@ -62,90 +62,98 @@ object OItem {
case class AttachmentData[F[_]](ra: RAttachment, meta: FileMeta, data: Stream[F, Byte])
def apply[F[_]: Effect](store: Store[F]): Resource[F, OItem[F]] =
Resource.pure(new OItem[F] {
def findItem(id: Ident, collective: Ident): F[Option[ItemData]] =
store.transact(QItem.findItem(id)).
map(opt => opt.flatMap(_.filterCollective(collective)))
store.transact(QItem.findItem(id)).map(opt => opt.flatMap(_.filterCollective(collective)))
def findItems(q: Query, maxResults: Int): F[Vector[ListItem]] = {
def findItems(q: Query, maxResults: Int): F[Vector[ListItem]] =
store.transact(QItem.findItems(q).take(maxResults.toLong)).compile.toVector
}
def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]] = {
store.transact(RAttachment.findByIdAndCollective(id, collective)).
flatMap({
def findAttachment(id: Ident, collective: Ident): F[Option[AttachmentData[F]]] =
store
.transact(RAttachment.findByIdAndCollective(id, collective))
.flatMap({
case Some(ra) =>
store.bitpeace.get(ra.fileId.id).unNoneTerminate.compile.last.
map(_.map(m => AttachmentData[F](ra, m, store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m)))))
store.bitpeace
.get(ra.fileId.id)
.unNoneTerminate
.compile
.last
.map(
_.map(m =>
AttachmentData[F](
ra,
m,
store.bitpeace.fetchData2(RangeDef.all)(Stream.emit(m))
)
)
)
case None =>
(None: Option[AttachmentData[F]]).pure[F]
})
}
def setTags(item: Ident, tagIds: List[Ident], collective: Ident): F[AddResult] = {
val db = for {
cid <- RItem.getCollective(item)
nd <- if (cid.contains(collective)) RTagItem.deleteItemTags(item) else 0.pure[ConnectionIO]
ni <- if (tagIds.nonEmpty && cid.contains(collective)) RTagItem.insertItemTags(item, tagIds) else 0.pure[ConnectionIO]
nd <- if (cid.contains(collective)) RTagItem.deleteItemTags(item)
else 0.pure[ConnectionIO]
ni <- if (tagIds.nonEmpty && cid.contains(collective))
RTagItem.insertItemTags(item, tagIds)
else 0.pure[ConnectionIO]
} yield nd + ni
store.transact(db).
attempt.
map(AddResult.fromUpdate)
store.transact(db).attempt.map(AddResult.fromUpdate)
}
def setDirection(item: Ident, direction: Direction, collective: Ident): F[AddResult] =
store.transact(RItem.updateDirection(item, collective, direction)).
attempt.
map(AddResult.fromUpdate)
store
.transact(RItem.updateDirection(item, collective, direction))
.attempt
.map(AddResult.fromUpdate)
def setCorrOrg(item: Ident, org: Option[Ident], collective: Ident): F[AddResult] =
store.transact(RItem.updateCorrOrg(item, collective, org)).
attempt.
map(AddResult.fromUpdate)
store.transact(RItem.updateCorrOrg(item, collective, org)).attempt.map(AddResult.fromUpdate)
def setCorrPerson(item: Ident, person: Option[Ident], collective: Ident): F[AddResult] =
store.transact(RItem.updateCorrPerson(item, collective, person)).
attempt.
map(AddResult.fromUpdate)
store
.transact(RItem.updateCorrPerson(item, collective, person))
.attempt
.map(AddResult.fromUpdate)
def setConcPerson(item: Ident, person: Option[Ident], collective: Ident): F[AddResult] =
store.transact(RItem.updateConcPerson(item, collective, person)).
attempt.
map(AddResult.fromUpdate)
store
.transact(RItem.updateConcPerson(item, collective, person))
.attempt
.map(AddResult.fromUpdate)
def setConcEquip(item: Ident, equip: Option[Ident], collective: Ident): F[AddResult] =
store.transact(RItem.updateConcEquip(item, collective, equip)).
attempt.
map(AddResult.fromUpdate)
store
.transact(RItem.updateConcEquip(item, collective, equip))
.attempt
.map(AddResult.fromUpdate)
def setNotes(item: Ident, notes: Option[String], collective: Ident): F[AddResult] =
store.transact(RItem.updateNotes(item, collective, notes)).
attempt.
map(AddResult.fromUpdate)
store.transact(RItem.updateNotes(item, collective, notes)).attempt.map(AddResult.fromUpdate)
def setName(item: Ident, name: String, collective: Ident): F[AddResult] =
store.transact(RItem.updateName(item, collective, name)).
attempt.
map(AddResult.fromUpdate)
store.transact(RItem.updateName(item, collective, name)).attempt.map(AddResult.fromUpdate)
def setState(item: Ident, state: ItemState, collective: Ident): F[AddResult] =
store.transact(RItem.updateStateForCollective(item, state, collective)).
attempt.
map(AddResult.fromUpdate)
store
.transact(RItem.updateStateForCollective(item, state, collective))
.attempt
.map(AddResult.fromUpdate)
def setItemDate(item: Ident, date: Option[Timestamp], collective: Ident): F[AddResult] =
store.transact(RItem.updateDate(item, collective, date)).
attempt.
map(AddResult.fromUpdate)
store.transact(RItem.updateDate(item, collective, date)).attempt.map(AddResult.fromUpdate)
def setItemDueDate(item: Ident, date: Option[Timestamp], collective: Ident): F[AddResult] =
store.transact(RItem.updateDueDate(item, collective, date)).
attempt.
map(AddResult.fromUpdate)
store
.transact(RItem.updateDueDate(item, collective, date))
.attempt
.map(AddResult.fromUpdate)
def delete(itemId: Ident, collective: Ident): F[Int] =
QItem.delete(store)(itemId, collective)

View File

@ -21,9 +21,9 @@ object OJob {
sealed trait JobCancelResult
object JobCancelResult {
case object Removed extends JobCancelResult
case object Removed extends JobCancelResult
case object CancelRequested extends JobCancelResult
case object JobNotFound extends JobCancelResult
case object JobNotFound extends JobCancelResult
}
case class JobDetail(job: RJob, logs: Vector[RJobLog])
@ -36,15 +36,19 @@ object OJob {
jobs.filter(_.job.state == JobState.Running)
}
def apply[F[_]: ConcurrentEffect](store: Store[F], clientEC: ExecutionContext): Resource[F, OJob[F]] =
def apply[F[_]: ConcurrentEffect](
store: Store[F],
clientEC: ExecutionContext
): Resource[F, OJob[F]] =
Resource.pure(new OJob[F] {
def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState] = {
store.transact(QJob.queueStateSnapshot(collective).take(maxResults.toLong)).
map(t => JobDetail(t._1, t._2)).
compile.toVector.
map(CollectiveQueueState)
}
def queueState(collective: Ident, maxResults: Int): F[CollectiveQueueState] =
store
.transact(QJob.queueStateSnapshot(collective).take(maxResults.toLong))
.map(t => JobDetail(t._1, t._2))
.compile
.toVector
.map(CollectiveQueueState)
def cancelJob(id: Ident, collective: Ident): F[JobCancelResult] = {
def mustCancel(job: Option[RJob]): Option[(RJob, Ident)] =
@ -58,26 +62,27 @@ object OJob {
val tryDelete = for {
job <- RJob.findByIdAndGroup(id, collective)
jobm = job.filter(canDelete)
jobm = job.filter(canDelete)
del <- jobm.traverse(j => RJob.delete(j.id))
} yield del match {
case Some(_) => Right(JobCancelResult.Removed: JobCancelResult)
case None => Left(mustCancel(job))
case None => Left(mustCancel(job))
}
def tryCancel(job: RJob, worker: Ident): F[JobCancelResult] =
OJoex.cancelJob(job.id, worker, store, clientEC).
map(flag => if (flag) JobCancelResult.CancelRequested else JobCancelResult.JobNotFound)
OJoex
.cancelJob(job.id, worker, store, clientEC)
.map(flag => if (flag) JobCancelResult.CancelRequested else JobCancelResult.JobNotFound)
for {
tryDel <- store.transact(tryDelete)
result <- tryDel match {
case Right(r) => r.pure[F]
case Left(Some((job, worker))) =>
tryCancel(job, worker)
case Left(None) =>
(JobCancelResult.JobNotFound: OJob.JobCancelResult).pure[F]
}
tryDel <- store.transact(tryDelete)
result <- tryDel match {
case Right(r) => r.pure[F]
case Left(Some((job, worker))) =>
tryCancel(job, worker)
case Left(None) =>
(JobCancelResult.JobNotFound: OJob.JobCancelResult).pure[F]
}
} yield result
}
})

View File

@ -13,24 +13,32 @@ import scala.concurrent.ExecutionContext
import org.log4s._
object OJoex {
private [this] val logger = getLogger
private[this] val logger = getLogger
def notifyAll[F[_]: ConcurrentEffect](store: Store[F], clientExecutionContext: ExecutionContext): F[Unit] = {
def notifyAll[F[_]: ConcurrentEffect](
store: Store[F],
clientExecutionContext: ExecutionContext
): F[Unit] =
for {
nodes <- store.transact(RNode.findAll(NodeType.Joex))
_ <- nodes.toList.traverse(notifyJoex[F](clientExecutionContext))
} yield ()
}
def cancelJob[F[_]: ConcurrentEffect](jobId: Ident, worker: Ident, store: Store[F], clientEc: ExecutionContext): F[Boolean] =
def cancelJob[F[_]: ConcurrentEffect](
jobId: Ident,
worker: Ident,
store: Store[F],
clientEc: ExecutionContext
): F[Boolean] =
for {
node <- store.transact(RNode.findById(worker))
node <- store.transact(RNode.findById(worker))
cancel <- node.traverse(joexCancel(clientEc)(_, jobId))
} yield cancel.getOrElse(false)
private def joexCancel[F[_]: ConcurrentEffect](ec: ExecutionContext)(node: RNode, job: Ident): F[Boolean] = {
val notifyUrl = node.url/"api"/"v1"/"job"/job.id/"cancel"
private def joexCancel[F[_]: ConcurrentEffect](
ec: ExecutionContext
)(node: RNode, job: Ident): F[Boolean] = {
val notifyUrl = node.url / "api" / "v1" / "job" / job.id / "cancel"
BlazeClientBuilder[F](ec).resource.use { client =>
val req = Request[F](POST, Uri.unsafeFromString(notifyUrl.asString))
client.expect[String](req).map(_ => true)
@ -38,7 +46,7 @@ object OJoex {
}
private def notifyJoex[F[_]: ConcurrentEffect](ec: ExecutionContext)(node: RNode): F[Unit] = {
val notifyUrl = node.url/"api"/"v1"/"notify"
val notifyUrl = node.url / "api" / "v1" / "notify"
val execute = BlazeClientBuilder[F](ec).resource.use { client =>
val req = Request[F](POST, Uri.unsafeFromString(notifyUrl.asString))
client.expect[String](req).map(_ => ())

View File

@ -36,13 +36,15 @@ object OOrganization {
case class PersonAndContacts(person: RPerson, contacts: Seq[RContact])
def apply[F[_] : Effect](store: Store[F]): Resource[F, OOrganization[F]] =
def apply[F[_]: Effect](store: Store[F]): Resource[F, OOrganization[F]] =
Resource.pure(new OOrganization[F] {
def findAllOrg(account: AccountId): F[Vector[OrgAndContacts]] =
store.transact(QOrganization.findOrgAndContact(account.collective, _.name)).
map({ case (org, cont) => OrgAndContacts(org, cont) }).
compile.toVector
store
.transact(QOrganization.findOrgAndContact(account.collective, _.name))
.map({ case (org, cont) => OrgAndContacts(org, cont) })
.compile
.toVector
def findAllOrgRefs(account: AccountId): F[Vector[IdRef]] =
store.transact(ROrganization.findAllRef(account.collective, _.name))
@ -54,9 +56,11 @@ object OOrganization {
QOrganization.updateOrg(s.org, s.contacts, s.org.cid)(store)
def findAllPerson(account: AccountId): F[Vector[PersonAndContacts]] =
store.transact(QOrganization.findPersonAndContact(account.collective, _.name)).
map({ case (person, cont) => PersonAndContacts(person, cont) }).
compile.toVector
store
.transact(QOrganization.findPersonAndContact(account.collective, _.name))
.map({ case (person, cont) => PersonAndContacts(person, cont) })
.compile
.toVector
def findAllPersonRefs(account: AccountId): F[Vector[IdRef]] =
store.transact(RPerson.findAllRef(account.collective, _.name))
@ -68,14 +72,13 @@ object OOrganization {
QOrganization.updatePerson(s.person, s.contacts, s.person.cid)(store)
def deleteOrg(orgId: Ident, collective: Ident): F[AddResult] =
store.transact(QOrganization.deleteOrg(orgId, collective)).
attempt.
map(AddResult.fromUpdate)
store.transact(QOrganization.deleteOrg(orgId, collective)).attempt.map(AddResult.fromUpdate)
def deletePerson(personId: Ident, collective: Ident): F[AddResult] =
store.transact(QOrganization.deletePerson(personId, collective)).
attempt.
map(AddResult.fromUpdate)
store
.transact(QOrganization.deletePerson(personId, collective))
.attempt
.map(AddResult.fromUpdate)
})
}

View File

@ -41,8 +41,6 @@ object OSource {
}
def delete(id: Ident, collective: Ident): F[AddResult] =
store.transact(RSource.delete(id, collective)).
attempt.
map(AddResult.fromUpdate)
store.transact(RSource.delete(id, collective)).attempt.map(AddResult.fromUpdate)
})
}

View File

@ -17,7 +17,6 @@ trait OTag[F[_]] {
def delete(id: Ident, collective: Ident): F[AddResult]
}
object OTag {
def apply[F[_]: Effect](store: Store[F]): Resource[F, OTag[F]] =
@ -47,10 +46,7 @@ object OTag {
n0 <- optTag.traverse(t => RTagItem.deleteTag(t.tagId))
n1 <- optTag.traverse(t => RTag.delete(t.tagId, collective))
} yield n0.getOrElse(0) + n1.getOrElse(0)
store.transact(io).
attempt.
map(AddResult.fromUpdate)
store.transact(io).attempt.map(AddResult.fromUpdate)
}
})
}

View File

@ -22,75 +22,113 @@ trait OUpload[F[_]] {
}
object OUpload {
private [this] val logger = getLogger
private[this] val logger = getLogger
case class File[F[_]](name: Option[String], advertisedMime: Option[MimeType], data: Stream[F, Byte])
case class File[F[_]](
name: Option[String],
advertisedMime: Option[MimeType],
data: Stream[F, Byte]
)
case class UploadMeta( direction: Option[Direction]
, sourceAbbrev: String
, validFileTypes: Seq[MimeType])
case class UploadMeta(
direction: Option[Direction],
sourceAbbrev: String,
validFileTypes: Seq[MimeType]
)
case class UploadData[F[_]]( multiple: Boolean
, meta: UploadMeta
, files: Vector[File[F]], priority: Priority, tracker: Option[Ident])
case class UploadData[F[_]](
multiple: Boolean,
meta: UploadMeta,
files: Vector[File[F]],
priority: Priority,
tracker: Option[Ident]
)
sealed trait UploadResult
object UploadResult {
case object Success extends UploadResult
case object NoFiles extends UploadResult
case object Success extends UploadResult
case object NoFiles extends UploadResult
case object NoSource extends UploadResult
}
def apply[F[_]: ConcurrentEffect](store: Store[F], queue: JobQueue[F], cfg: Config, httpClientEC: ExecutionContext): Resource[F, OUpload[F]] =
def apply[F[_]: ConcurrentEffect](
store: Store[F],
queue: JobQueue[F],
cfg: Config,
httpClientEC: ExecutionContext
): Resource[F, OUpload[F]] =
Resource.pure(new OUpload[F] {
def submit(data: OUpload.UploadData[F], account: AccountId): F[OUpload.UploadResult] = {
def submit(data: OUpload.UploadData[F], account: AccountId): F[OUpload.UploadResult] =
for {
files <- data.files.traverse(saveFile).map(_.flatten)
pred <- checkFileList(files)
lang <- store.transact(RCollective.findLanguage(account.collective))
meta = ProcessItemArgs.ProcessMeta(account.collective, lang.getOrElse(Language.German), data.meta.direction, data.meta.sourceAbbrev, data.meta.validFileTypes)
args = if (data.multiple) files.map(f => ProcessItemArgs(meta, List(f))) else Vector(ProcessItemArgs(meta, files.toList))
job <- pred.traverse(_ => makeJobs(args, account, data.priority, data.tracker))
_ <- logger.fdebug(s"Storing jobs: $job")
res <- job.traverse(submitJobs)
_ <- store.transact(RSource.incrementCounter(data.meta.sourceAbbrev, account.collective))
meta = ProcessItemArgs.ProcessMeta(
account.collective,
lang.getOrElse(Language.German),
data.meta.direction,
data.meta.sourceAbbrev,
data.meta.validFileTypes
)
args = if (data.multiple) files.map(f => ProcessItemArgs(meta, List(f)))
else Vector(ProcessItemArgs(meta, files.toList))
job <- pred.traverse(_ => makeJobs(args, account, data.priority, data.tracker))
_ <- logger.fdebug(s"Storing jobs: $job")
res <- job.traverse(submitJobs)
_ <- store.transact(RSource.incrementCounter(data.meta.sourceAbbrev, account.collective))
} yield res.fold(identity, identity)
}
def submit(data: OUpload.UploadData[F], sourceId: Ident): F[OUpload.UploadResult] =
for {
sOpt <- store.transact(RSource.find(sourceId)).map(_.toRight(UploadResult.NoSource))
abbrev = sOpt.map(_.abbrev).toOption.getOrElse(data.meta.sourceAbbrev)
updata = data.copy(meta = data.meta.copy(sourceAbbrev = abbrev))
accId = sOpt.map(source => AccountId(source.cid, source.sid))
result <- accId.traverse(acc => submit(updata, acc))
sOpt <- store.transact(RSource.find(sourceId)).map(_.toRight(UploadResult.NoSource))
abbrev = sOpt.map(_.abbrev).toOption.getOrElse(data.meta.sourceAbbrev)
updata = data.copy(meta = data.meta.copy(sourceAbbrev = abbrev))
accId = sOpt.map(source => AccountId(source.cid, source.sid))
result <- accId.traverse(acc => submit(updata, acc))
} yield result.fold(identity, identity)
private def submitJobs(jobs: Vector[RJob]): F[OUpload.UploadResult] = {
private def submitJobs(jobs: Vector[RJob]): F[OUpload.UploadResult] =
for {
_ <- logger.fdebug(s"Storing jobs: $jobs")
_ <- queue.insertAll(jobs)
_ <- OJoex.notifyAll(store, httpClientEC)
_ <- logger.fdebug(s"Storing jobs: $jobs")
_ <- queue.insertAll(jobs)
_ <- OJoex.notifyAll(store, httpClientEC)
} yield UploadResult.Success
}
private def saveFile(file: File[F]): F[Option[ProcessItemArgs.File]] = {
private def saveFile(file: File[F]): F[Option[ProcessItemArgs.File]] =
logger.finfo(s"Receiving file $file") *>
store.bitpeace.saveNew(file.data, cfg.files.chunkSize, MimetypeHint(file.name, None), None).
compile.lastOrError.map(fm => Ident.unsafe(fm.id)).attempt.
map(_.fold(ex => {
logger.warn(ex)(s"Could not store file for processing!")
None
}, id => Some(ProcessItemArgs.File(file.name, id))))
}
store.bitpeace
.saveNew(file.data, cfg.files.chunkSize, MimetypeHint(file.name, None), None)
.compile
.lastOrError
.map(fm => Ident.unsafe(fm.id))
.attempt
.map(_.fold(ex => {
logger.warn(ex)(s"Could not store file for processing!")
None
}, id => Some(ProcessItemArgs.File(file.name, id))))
private def checkFileList(files: Seq[ProcessItemArgs.File]): F[Either[UploadResult, Unit]] =
Effect[F].pure(if (files.isEmpty) Left(UploadResult.NoFiles) else Right(()))
private def makeJobs(args: Vector[ProcessItemArgs], account: AccountId, prio: Priority, tracker: Option[Ident]): F[Vector[RJob]] = {
private def makeJobs(
args: Vector[ProcessItemArgs],
account: AccountId,
prio: Priority,
tracker: Option[Ident]
): F[Vector[RJob]] = {
def create(id: Ident, now: Timestamp, arg: ProcessItemArgs): RJob =
RJob.newJob(id, ProcessItemArgs.taskName, account.collective, arg, arg.makeSubject, now, account.user, prio, tracker)
RJob.newJob(
id,
ProcessItemArgs.taskName,
account.collective,
arg,
arg.makeSubject,
now,
account.user,
prio,
tracker
)
for {
id <- Ident.randomId[F]

View File

@ -20,10 +20,10 @@ object Config {
def fromString(str: String): Either[String, Mode] =
str.toLowerCase match {
case "open" => Right(Open)
case "open" => Right(Open)
case "invite" => Right(Invite)
case "closed" => Right(Closed)
case _ => Left(s"Invalid signup mode: $str")
case _ => Left(s"Invalid signup mode: $str")
}
def unsafe(str: String): Mode =
fromString(str).fold(sys.error, identity)
@ -34,7 +34,7 @@ object Config {
Decoder.decodeString.emap(fromString)
}
def open: Mode = Mode.Open
def open: Mode = Mode.Open
def invite: Mode = Mode.Invite
def closed: Mode = Mode.Closed

View File

@ -9,11 +9,11 @@ sealed trait NewInviteResult { self: Product =>
}
object NewInviteResult {
case class Success(id: Ident) extends NewInviteResult
case class Success(id: Ident) extends NewInviteResult
case object InvitationDisabled extends NewInviteResult
case object PasswordMismatch extends NewInviteResult
case object PasswordMismatch extends NewInviteResult
def passwordMismatch: NewInviteResult = PasswordMismatch
def invitationClosed: NewInviteResult = InvitationDisabled
def passwordMismatch: NewInviteResult = PasswordMismatch
def invitationClosed: NewInviteResult = InvitationDisabled
def success(id: Ident): NewInviteResult = Success(id)
}

View File

@ -21,19 +21,19 @@ trait OSignup[F[_]] {
object OSignup {
private[this] val logger = getLogger
def apply[F[_]:Effect](store: Store[F]): Resource[F, OSignup[F]] =
def apply[F[_]: Effect](store: Store[F]): Resource[F, OSignup[F]] =
Resource.pure(new OSignup[F] {
def newInvite(cfg: Config)(password: Password): F[NewInviteResult] = {
def newInvite(cfg: Config)(password: Password): F[NewInviteResult] =
if (cfg.mode == Config.Mode.Invite) {
if (cfg.newInvitePassword.isEmpty || cfg.newInvitePassword != password) NewInviteResult.passwordMismatch.pure[F]
if (cfg.newInvitePassword.isEmpty || cfg.newInvitePassword != password)
NewInviteResult.passwordMismatch.pure[F]
else store.transact(RInvitation.insertNew).map(ri => NewInviteResult.success(ri.id))
} else {
Effect[F].pure(NewInviteResult.invitationClosed)
}
}
def register(cfg: Config)(data: RegisterData): F[SignupResult] = {
def register(cfg: Config)(data: RegisterData): F[SignupResult] =
cfg.mode match {
case Config.Mode.Open =>
addUser(data).map(SignupResult.fromAddResult)
@ -45,11 +45,11 @@ object OSignup {
data.invite match {
case Some(inv) =>
for {
now <- Timestamp.current[F]
min = now.minus(cfg.inviteTime)
ok <- store.transact(RInvitation.useInvite(inv, min))
res <- if (ok) addUser(data).map(SignupResult.fromAddResult)
else SignupResult.invalidInvitationKey.pure[F]
now <- Timestamp.current[F]
min = now.minus(cfg.inviteTime)
ok <- store.transact(RInvitation.useInvite(inv, min))
res <- if (ok) addUser(data).map(SignupResult.fromAddResult)
else SignupResult.invalidInvitationKey.pure[F]
_ <- if (retryInvite(res))
logger.fdebug(s"Adding account failed ($res). Allow retry with invite.") *> store
.transact(
@ -61,7 +61,6 @@ object OSignup {
SignupResult.invalidInvitationKey.pure[F]
}
}
}
private def retryInvite(res: SignupResult): Boolean =
res match {
@ -77,29 +76,37 @@ object OSignup {
false
}
private def addUser(data: RegisterData): F[AddResult] = {
def toRecords: F[(RCollective, RUser)] =
for {
id2 <- Ident.randomId[F]
now <- Timestamp.current[F]
c = RCollective(data.collName, CollectiveState.Active, Language.German, now)
u = RUser(id2, data.login, data.collName, PasswordCrypt.crypt(data.password), UserState.Active, None, 0, None, now)
c = RCollective(data.collName, CollectiveState.Active, Language.German, now)
u = RUser(
id2,
data.login,
data.collName,
PasswordCrypt.crypt(data.password),
UserState.Active,
None,
0,
None,
now
)
} yield (c, u)
def insert(coll: RCollective, user: RUser): ConnectionIO[Int] = {
def insert(coll: RCollective, user: RUser): ConnectionIO[Int] =
for {
n1 <- RCollective.insert(coll)
n2 <- RUser.insert(user)
} yield n1 + n2
}
def collectiveExists: ConnectionIO[Boolean] =
RCollective.existsById(data.collName)
val msg = s"The collective '${data.collName}' already exists."
for {
cu <- toRecords
cu <- toRecords
save <- store.add(insert(cu._1, cu._2), collectiveExists)
} yield save.fold(identity, _.withMsg(msg), identity)
}

View File

@ -2,27 +2,25 @@ package docspell.backend.signup
import docspell.store.AddResult
sealed trait SignupResult {
}
sealed trait SignupResult {}
object SignupResult {
case object CollectiveExists extends SignupResult
case object InvalidInvitationKey extends SignupResult
case object SignupClosed extends SignupResult
case object CollectiveExists extends SignupResult
case object InvalidInvitationKey extends SignupResult
case object SignupClosed extends SignupResult
case class Failure(ex: Throwable) extends SignupResult
case object Success extends SignupResult
case object Success extends SignupResult
def collectiveExists: SignupResult = CollectiveExists
def invalidInvitationKey: SignupResult = InvalidInvitationKey
def signupClosed: SignupResult = SignupClosed
def collectiveExists: SignupResult = CollectiveExists
def invalidInvitationKey: SignupResult = InvalidInvitationKey
def signupClosed: SignupResult = SignupClosed
def failure(ex: Throwable): SignupResult = Failure(ex)
def success: SignupResult = Success
def success: SignupResult = Success
def fromAddResult(ar: AddResult): SignupResult = ar match {
case AddResult.Success => Success
case AddResult.Failure(ex) => Failure(ex)
case AddResult.Success => Success
case AddResult.Failure(ex) => Failure(ex)
case AddResult.EntityExists(_) => CollectiveExists
}
}