Starting with mail functionality

This commit is contained in:
Eike Kettner
2020-01-05 23:23:28 +01:00
parent 2e3454c7a1
commit f235f3a030
14 changed files with 853 additions and 56 deletions

View File

@ -0,0 +1,34 @@
package docspell.store
import emil._
import emil.javamail.syntax._
object EmilUtil {
def readSSLType(str: String): Either[String, SSLType] =
str.toLowerCase match {
case "ssl" => Right(SSLType.SSL)
case "starttls" => Right(SSLType.StartTLS)
case "none" => Right(SSLType.NoEncryption)
case _ => Left(s"Invalid ssl-type: $str")
}
def unsafeReadSSLType(str: String): SSLType =
readSSLType(str).fold(sys.error, identity)
def sslTypeString(st: SSLType): String =
st match {
case SSLType.SSL => "ssl"
case SSLType.StartTLS => "starttls"
case SSLType.NoEncryption => "none"
}
def readMailAddress(str: String): Either[String, MailAddress] =
MailAddress.parse(str)
def unsafeReadMailAddress(str: String): MailAddress =
readMailAddress(str).fold(sys.error, identity)
def mailAddressString(ma: MailAddress): String =
ma.asUnicodeString
}

View File

@ -2,18 +2,15 @@ package docspell.store.impl
import java.time.format.DateTimeFormatter
import java.time.{Instant, LocalDate}
import docspell.common.Timestamp
import docspell.common._
import io.circe.{Decoder, Encoder}
import doobie._
//import doobie.implicits.javatime._
import doobie.implicits.legacy.instant._
import doobie.util.log.Success
import io.circe.{Decoder, Encoder}
import docspell.common.syntax.all._
import emil.{MailAddress, SSLType}
import emil.javamail.syntax._
import docspell.common._
import docspell.common.syntax.all._
import docspell.store.EmilUtil
trait DoobieMeta {
@ -92,29 +89,14 @@ trait DoobieMeta {
Meta[String].imap(Language.unsafe)(_.iso3)
implicit val sslType: Meta[SSLType] =
Meta[String].imap(DoobieMeta.readSSLType)(DoobieMeta.sslTypeString)
Meta[String].imap(EmilUtil.unsafeReadSSLType)(EmilUtil.sslTypeString)
implicit val mailAddress: Meta[MailAddress] =
Meta[String].imap(str => MailAddress.parse(str).fold(sys.error, identity))(_.asUnicodeString)
Meta[String].imap(EmilUtil.unsafeReadMailAddress)(EmilUtil.mailAddressString)
}
object DoobieMeta extends DoobieMeta {
import org.log4s._
private val logger = getLogger
private def readSSLType(str: String): SSLType =
str.toLowerCase match {
case "ssl" => SSLType.SSL
case "starttls" => SSLType.StartTLS
case "none" => SSLType.NoEncryption
case _ => sys.error(s"Invalid ssl-type: $str")
}
private def sslTypeString(st: SSLType): String =
st match {
case SSLType.SSL => "ssl"
case SSLType.StartTLS => "starttls"
case SSLType.NoEncryption => "none"
}
}

View File

@ -2,6 +2,9 @@ package docspell.store.records
import doobie._
import doobie.implicits._
import cats.effect._
import cats.implicits._
import cats.data.OptionT
import docspell.common._
import docspell.store.impl.Column
import docspell.store.impl.Implicits._
@ -10,11 +13,11 @@ import emil.{MailAddress, SSLType}
case class RUserEmail(
id: Ident,
uid: Ident,
name: String,
name: Ident,
smtpHost: String,
smtpPort: Int,
smtpUser: String,
smtpPassword: Password,
smtpPort: Option[Int],
smtpUser: Option[String],
smtpPassword: Option[Password],
smtpSsl: SSLType,
smtpCertCheck: Boolean,
mailFrom: MailAddress,
@ -24,6 +27,67 @@ case class RUserEmail(
object RUserEmail {
def apply[F[_]: Sync](
uid: Ident,
name: Ident,
smtpHost: String,
smtpPort: Option[Int],
smtpUser: Option[String],
smtpPassword: Option[Password],
smtpSsl: SSLType,
smtpCertCheck: Boolean,
mailFrom: MailAddress,
mailReplyTo: Option[MailAddress]
): F[RUserEmail] =
for {
now <- Timestamp.current[F]
id <- Ident.randomId[F]
} yield RUserEmail(
id,
uid,
name,
smtpHost,
smtpPort,
smtpUser,
smtpPassword,
smtpSsl,
smtpCertCheck,
mailFrom,
mailReplyTo,
now
)
def apply(
accId: AccountId,
name: Ident,
smtpHost: String,
smtpPort: Option[Int],
smtpUser: Option[String],
smtpPassword: Option[Password],
smtpSsl: SSLType,
smtpCertCheck: Boolean,
mailFrom: MailAddress,
mailReplyTo: Option[MailAddress]
): OptionT[ConnectionIO, RUserEmail] =
for {
now <- OptionT.liftF(Timestamp.current[ConnectionIO])
id <- OptionT.liftF(Ident.randomId[ConnectionIO])
user <- OptionT(RUser.findByAccount(accId))
} yield RUserEmail(
id,
user.uid,
name,
smtpHost,
smtpPort,
smtpUser,
smtpPassword,
smtpSsl,
smtpCertCheck,
mailFrom,
mailReplyTo,
now
)
val table = fr"useremail"
object Columns {
@ -65,7 +129,54 @@ object RUserEmail {
sql"${v.id},${v.uid},${v.name},${v.smtpHost},${v.smtpPort},${v.smtpUser},${v.smtpPassword},${v.smtpSsl},${v.smtpCertCheck},${v.mailFrom},${v.mailReplyTo},${v.created}"
).update.run
def update(eId: Ident, v: RUserEmail): ConnectionIO[Int] =
updateRow(table, id.is(eId), commas(
name.setTo(v.name),
smtpHost.setTo(v.smtpHost),
smtpPort.setTo(v.smtpPort),
smtpUser.setTo(v.smtpUser),
smtpPass.setTo(v.smtpPassword),
smtpSsl.setTo(v.smtpSsl),
smtpCertCheck.setTo(v.smtpCertCheck),
mailFrom.setTo(v.mailFrom),
mailReplyTo.setTo(v.mailReplyTo)
)).update.run
def findByUser(userId: Ident): ConnectionIO[Vector[RUserEmail]] =
selectSimple(all, table, uid.is(userId)).query[RUserEmail].to[Vector]
private def findByAccount0(
accId: AccountId,
nameQ: Option[String],
exact: Boolean
): Query0[RUserEmail] = {
val mUid = uid.prefix("m")
val mName = name.prefix("m")
val uId = RUser.Columns.uid.prefix("u")
val uColl = RUser.Columns.cid.prefix("u")
val uLogin = RUser.Columns.login.prefix("u")
val from = table ++ fr"m INNER JOIN" ++ RUser.table ++ fr"u ON" ++ mUid.is(uId)
val cond = Seq(uColl.is(accId.collective), uLogin.is(accId.user)) ++ (nameQ match {
case Some(str) if exact => Seq(mName.is(str))
case Some(str) if !exact => Seq(mName.lowerLike(s"%${str.toLowerCase}%"))
case None => Seq.empty
})
selectSimple(all, from, and(cond)).query[RUserEmail]
}
def findByAccount(
accId: AccountId,
nameQ: Option[String]
): ConnectionIO[Vector[RUserEmail]] =
findByAccount0(accId, nameQ, false).to[Vector]
def getByName(accId: AccountId, name: Ident): ConnectionIO[Option[RUserEmail]] =
findByAccount0(accId, Some(name.id), true).option
def exists(accId: AccountId, name: Ident): ConnectionIO[Boolean] =
getByName(accId, name).map(_.isDefined)
def exists(userId: Ident, connName: Ident): ConnectionIO[Boolean] =
selectCount(id, table, and(uid.is(userId), name.is(connName))).query[Int].unique.map(_ > 0)
}