Skip to content

Commit

Permalink
feat: email service and some common stuff WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jona7o committed Feb 11, 2022
1 parent b1b09c9 commit 4142357
Show file tree
Hide file tree
Showing 14 changed files with 285 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .bsp/sbt.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"name":"sbt","version":"1.5.0","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["/Users/patrick/Library/Java/JavaVirtualMachines/azul-11.0.13-1/Contents/Home/bin/java","-Xms100m","-Xmx100m","-classpath","/Users/patrick/Library/Application Support/JetBrains/IntelliJIdea2021.3/plugins/Scala/launcher/sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=/Users/patrick/Library/Application%20Support/JetBrains/IntelliJIdea2021.3/plugins/Scala/launcher/sbt-launch.jar"]}
{"name":"sbt","version":"1.5.0","bspVersion":"2.0.0-M5","languages":["scala"],"argv":["/Users/jona7o/.sdkman/candidates/java/11.0.13.8.1-amzn/bin/java","-Xms100m","-Xmx100m","-classpath","/Users/jona7o/Library/Application Support/JetBrains/IntelliJIdea2021.2/plugins/Scala/launcher/sbt-launch.jar","xsbt.boot.Boot","-bsp","--sbt-launch-jar=/Users/jona7o/Library/Application%20Support/JetBrains/IntelliJIdea2021.2/plugins/Scala/launcher/sbt-launch.jar"]}
27 changes: 27 additions & 0 deletions .github/workflows/compile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Compile Sources
on:
push:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache SBT ivy cache
uses: actions/cache@v1
with:
path: ~/.ivy2/cache
key: ${{ runner.os }}-sbt-ivy-cache-${{ hashFiles('**/build.sbt') }}
- name: Cache SBT
uses: actions/cache@v1
with:
path: ~/.sbt
key: ${{ runner.os }}-sbt-${{ hashFiles('**/build.sbt') }}
- name: Set up JDK 11.0.10
uses: actions/setup-java@v1
with:
java-version: 11.0.10
- name: Compile
run: |
sbt compile
env:
GITHUB_TOKEN: ${{ secrets.PUBLIC_GITHUB_TOKEN }}
13 changes: 10 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ val githubSettings = Seq(
)

val defaultProjectSettings = Seq(
scalaVersion := "2.13.3",
scalaVersion := "2.13.8",
organization := "de.innfactory.scala-utils",
version := releaseVersion,
githubOwner := "innFactory",
Expand Down Expand Up @@ -93,7 +93,9 @@ val slickJodaMapper = "com.github.tototoshi" %% "slick-joda-mapper" % "2.4.2"
val flyWayCore = "org.flywaydb" % "flyway-core" % "8.4.1"
val joda = "joda-time" % "joda-time" % "2.10.13"

val typesafePlay = "com.typesafe.play" %% "play" % "2.8.13"
val playVersion = "2.8.13"
val typesafePlay = "com.typesafe.play" %% "play" % playVersion
val playWs = "com.typesafe.play" %% "play-ws" % playVersion
val playJson = "com.typesafe.play" %% "play-json" % "2.9.2"

val scalaOpencensus = "com.github.sebruck" %% "opencensus-scala-core" % "0.7.2"
Expand All @@ -108,6 +110,7 @@ val sharedDeps = "com.google.cloud" % "google-cloud-shared-dependencies" % "2.5.
val logback = "ch.qos.logback" % "logback-classic" % "1.2.10"
val logbackCore = "ch.qos.logback" % "logback-core" % "1.2.10"

val guice = "com.google.inject" % "guice" % "4.2.3"

lazy val play = (project in file("util-play"))
.settings(
Expand Down Expand Up @@ -135,9 +138,13 @@ lazy val play = (project in file("util-play"))
slickCodegen,
slickHikaricp,
hikariCP,
flyWayCore
flyWayCore,
guice,
playWs
)
)
.dependsOn(utilImplicits)
.aggregate(utilImplicits)

lazy val root = project
.in(file("."))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package de.innfactory.implicits

object OptionUtils {
implicit class EnhancedOption[T](value: Option[T]) {
def getOrElseOld(oldOption: Option[T]): Option[T] =
value match {
case Some(of) => Some(of)
case None => oldOption
}

def toEither[L](leftResult: L): Either[L, T] =
value match {
case Some(v) => Right(v)
case None => Left(leftResult)
}

def toInverseEither[L](leftResult: L): Either[L, String] =
value match {
case Some(_) => Left(leftResult)
case None => Right("")
}

def toResult[L](leftResult: L): Either[L, T] =
if (value.isDefined) Right(value.get) else Left(leftResult)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package de.innfactory.implicits

trait Showable { this: Product =>

def show: String = {
val className = this.productPrefix
val fieldNames = this.productElementNames.toList
val fieldValues = this.productIterator.toList
val fields = fieldNames.zip(fieldValues).map { case (name, value) => s"$name = $value" }
fields.mkString(s"$className(", ", ", ")")
}
override def toString: String = show
}
23 changes: 23 additions & 0 deletions util-play/src/main/scala/de/innfactory/play/mail/Mail.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package de.innfactory.play.mail

import play.api.libs.json.{JsObject, Json}


case class Mail(
recipients: Seq[String],
toCC: Option[Seq[String]],
toBCC: Option[Seq[String]],
replyTo: Option[String],
body: String,
fromName: String,
subject: String,
fromEmail: String
) {

def toRequestJson: JsObject = ???

}

object Mail {
implicit val format = Json.format[Mail]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package de.innfactory.play.mail

case class MailConfig(endpoint: String, apiKey: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package de.innfactory.play.mail

import play.api.libs.json.Json

case class MailResponse(id: String)

object MailResponse {
implicit val format = Json.format[MailResponse]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.innfactory.play.mail

import de.innfactory.implicits.Showable
import de.innfactory.play.controller.ErrorResult

case class MailSendError(
message: String = "Mailservice had a problem while sending the mail",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 503
) extends ErrorResult()
with Showable
23 changes: 23 additions & 0 deletions util-play/src/main/scala/de/innfactory/play/mail/MailService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package de.innfactory.play.mail

import cats.data.EitherT

import scala.concurrent.Future
import com.google.inject.ImplementedBy
import de.innfactory.implicits.OptionUtils.EnhancedOption
import de.innfactory.play.controller.ResultStatus
import de.innfactory.play.results.Results.Result
import play.api.Logger

@ImplementedBy(classOf[MailServiceImpl])
trait MailService {

val logger = Logger("mail").logger

def sendF(mail: Mail): Future[Option[MailResponse]]

def sendE(mail: Mail): Future[Result[MailResponse]]

def sendET(mail: Mail): EitherT[Future, Result, MailResponse] = EitherT(sendE(mail))

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package de.innfactory.play.mail
import de.innfactory.implicits.OptionUtils.EnhancedOption
import de.innfactory.play.controller.ResultStatus
import de.innfactory.play.results.Results
import de.innfactory.play.results.Results.Result
import play.api.libs.ws.{WSClient, WSRequest}

import javax.inject.Inject
import scala.concurrent.{ExecutionContext, Future}

class MailServiceImpl @Inject()(mailConfig: MailConfig, wsClient: WSClient)(implicit ec: ExecutionContext) extends MailService {


override def sendF(mail: Mail): Future[Option[MailResponse]] = {
val request: WSRequest = wsClient.url(mailConfig.endpoint + "/v1/sendmail")
request.post(mail.toRequestJson)
.map(response => {
if (response.status == 200) {
response.json.asOpt[MailResponse]
} else {
None
}
})
}

override def sendE(mail: Mail): Future[Result[MailResponse]] = sendF(mail).map(_.toResult(MailSendError())) // TODO: implement error handling
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package de.innfactory.play.results

import de.innfactory.play.controller.ResultStatus

object Results {

type Result[T] = Either[ResultStatus, T]

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package de.innfactory.play.results.errors

import de.innfactory.play.controller.ErrorResponse
import play.api.libs.json.{JsValue, Json}
import play.api.mvc.{AnyContent, Request}

case class ErrorResponseWithAdditionalBody(message: String, details: JsValue) {
def toJson = Json.toJson(this)(ErrorResponseWithAdditionalBody.writes)
}

object ErrorResponseWithAdditionalBody {

implicit val reads = Json.reads[ErrorResponseWithAdditionalBody]
implicit val writes = Json.writes[ErrorResponseWithAdditionalBody]

def fromRequest(message: String)(implicit request: Request[AnyContent]) =
Json.toJson(ErrorResponse(message))

def fromMessage(message: String) =
Json.toJson(ErrorResponse(message))

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package de.innfactory.play.results.errors

import de.innfactory.implicits.Showable
import de.innfactory.play.controller.ErrorResult

object Errors {

case class DatabaseResult(
message: String = "Entity or request malformed",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 500
) extends ErrorResult()
with Showable

case class BadRequest(
message: String = "Entity or request malformed",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 400
) extends ErrorResult()
with Showable

case class NotFound(
message: String = "Entity not found",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 404
) extends ErrorResult()
with Showable

case class Forbidden(
message: String = "Forbidden",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 403
) extends ErrorResult()
with Showable

case class TokenValidationError(
message: String = "TokenValidationError",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 400
) extends ErrorResult()
with Showable

case class TokenExpiredError(
message: String = "TokenExpiredError",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 410
) extends ErrorResult()
with Showable

case class BadGateway(
message: String = "BadGateway",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 502
) extends ErrorResult()
with Showable

case class InternalServerError(
message: String = "InternalServerError",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 500
) extends ErrorResult()
with Showable

case class TooManyRequests(
message: String = "TooManyRequests",
additionalInfoToLog: Option[String] = None,
additionalInfoErrorCode: Option[String] = None,
statusCode: Int = 429
) extends ErrorResult()
with Showable

}

0 comments on commit 4142357

Please sign in to comment.