Akka Http - multiroutes and simple reflection of code with implicits - scala

I have a simple Main object, which create routes for my two services this way:
object GameApp extends App {
val config = ConfigFactory.load()
val host = config.getString("http.host")
val port = config.getInt("http.port")
implicit val system = ActorSystem("game-service")
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
val game = new GameRouting
val rate = new RateRouting
val routes: Route = game.route ~ rate.route
Http().bindAndHandle(routes, host, port) map {
binding => println(s"Server start on ${binding.localAddress}")
} recover {
case ex => println(s"Server could not start on ${host}:${port}", ex.getMessage)
}
}
Now, I would like to refactor this code and move this piece into separate method/class/object etc.:
val game = new GameRouting
val rate = new RateRouting
val routes: Route = game.route ~ rate.route
But this classes (GameRouting and RateRouting) uses implicit in constructors and I cannot simply move this code to separate place.
How should I refactor this code to get what I want?
My another question is - routes classes (GameRouting and RateRouting) should be class or case class? This is GameRouting class:
trait Protocols extends SprayJsonSupport with DefaultJsonProtocol {
implicit val gameFormat = jsonFormat4(Game)
}
class GameRouting(implicit executionContext: ExecutionContext) extends Protocols {
val gameService = new GameService
val route: Route = {
pathPrefix("games") {
pathEndOrSingleSlash {
get {
complete {
gameService.getGames()
}
} ~
post {
entity(as[Game]) { gameForCreate =>
createGame(gameForCreate)
}
}
} ~
pathPrefix(LongNumber) { id =>
get {
complete {
gameService.getGame(id)
}
} ~ put {
entity(as[Game]) { gameForUpdate =>
complete {
gameService.update(gameForUpdate)
}
}
}
}
}
}
private def createGame(gameForCreate: Game) = {
onComplete(gameService.createGame(gameForCreate)) {
case Success(created) => complete(StatusCodes.Created, created)
case Failure(exception) => complete(StatusCodes.BadRequest, s"An error: $exception")
}
}
}
And if I make it case I cannot access field routes from Main object. How to fix this or write it in better way? Maybe there are basic questions, but I've been still learning Scala and Akka since few weeks.

You can just move your implicits to separate object:
object GameAppImplicits {
implicit val system = ActorSystem("game-service")
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
}
and then import when needed:
import yourpackage.GameAppImplicits._
For your second question, case classes are usually used for modeling immutable data. In this case you don't really need any feature, that case classes give you (like automatic toString, equals, copy etc.), so I would say it would be best to just use plain class.

Related

How to split routes in Akka Http

What is the best way to split routes that need implicit ActorSystem to different files? Suppose I have this (non-working) code
// in UserRoutes.scala
object UserRoutes {
val route: Route = ???
}
// in OrderRoutes.scala
object OrderRoutes {
val route: Route = ???
}
// In MyApp.scala
object MyApp extends App {
implicit val system = ActorSystem(Behaviors.empty, "my-system")
implicit val ec = system.executionContext
val route = UserRoutes.route ~ OrderRoutes.route
Http().newServerAt("localhost", 8888).bind(route)
}
All these routes make HTTP requests so they require the presence of ActorSystem, what is the best way to pass it? Make objects into classes and pass it to constructor or is there something more clever?
You can have something like
// in UserRoutes.scala
object UserRoutes {
def route(implicit sys: ActorSystem): Route = ???
}
// in OrderRoutes.scala
object OrderRoutes {
def route(implicit sys: ActorSystem): Route = ???
}
In your app, you have the actor system implicitly and then you will be able ton keep val route = UserRoutes.route ~ OrderRoutes.route as it is.
I would usually use a class if I have to use some services
class UserRoutes(auth: AuthService, profile: ProfileService)(implicit sys: ActorSystem) {
val route : Route = ???
}

How to integrate akka streams kafka (reactive-kafka) into akka http application?

I have a basic scala akka http CRUD application. See below for the relevant classes.
I'd simply like to write an entity id and some data (as json) to a Kafka topic whenever, for example, an entity is created/updated.
I'm looking at http://doc.akka.io/docs/akka-stream-kafka/current/producer.html, but am new to scala and akka, and unsure of how to integrate it into my application?
For example, from the docs above, this is the example of a producer writing to kafka, so I think I need to something similar, but whereabouts in my application should this go? Can I just add another map call in the create method in my service after I have created the user?
Many thanks!
val done = Source(1 to 100)
.map(_.toString)
.map { elem =>
new ProducerRecord[Array[Byte], String]("topic1", elem)
}
.runWith(Producer.plainSink(producerSettings))
Or do I need to do something like the example here https://github.com/hseeberger/accessus in the bindAndHandle() method in my Server.scala?
WebServer.scala
object System {
implicit val system = ActorSystem()
implicit val dispatcher = system.dispatcher
implicit val actorMaterializer = ActorMaterializer()
}
object WebServer extends App {
import System._
val config = new ApplicationConfig() with ConfigLoader
ConfigurationFactory.setConfigurationFactory(new LoggingConfFileConfigurationFactory(config.loggingConfig))
val injector = Guice.createInjector(new MyAppModule(config))
val routes = injector.getInstance(classOf[Router]).routes
Http().bindAndHandle(routes, config.httpConfig.interface, config.httpConfig.port)
}
Router.scala
def routes(): Route = {
post {
entity(as[User]) { user =>
val createUser = userService.create(user)
onSuccess(createUser) {
case Invalid(y: NonEmptyList[Err]) => {
throw new ValidationException(y)
}
case Valid(u: User) => {
complete(ToResponseMarshallable((StatusCodes.Created, u)))
}
}
}
} ~
// More routes here, left out for example
}
Service.scala
def create(user: User): Future[MaybeValid[User]] = {
for {
validating <- userValidation.validateCreate(user)
result <- validating match {
case Valid(x: User) =>
userRepo.create(x)
.map(dbUser => Valid(UserConverters.fromUserRow(x)))
case y: DefInvalid =>
Future{y}
}
} yield result
}
Repo.scala
def create(user: User): Future[User] = {
mutateDbProvider.db.run(
userTable returning userTable.map(_.userId)
into ((user, id) => user.copy(userId = id)) +=
user.copy(createdDate = Some(Timestamp.valueOf(LocalDateTime.now())))
)
}
Since you have written your Route to unmarshall just 1 User from the Entity I don't think you need Producer.plainSink. Rather, I think Producer.send will work just as well. Also, as a side note, throwing exceptions is not "idiomatic" scala. So I changed the logic for invalid user:
val producer : KafkaProducer = new KafkaProducer(producerSettings)
val routes : Route =
post {
entity(as[User]) { user =>
val createUser = userService.create(user)
onSuccess(createUser) {
case Invalid(y: NonEmptyList[Err]) =>
complete(BadRequest -> "invalid user")
case Valid(u: User) => {
val producerRecord =
new ProducerRecord[Array[Byte], String]("topic1",s"""{"userId" : ${u.userId}, "entity" : "User"}""")
onComplete(producer send producerRecord) { _ =>
complete(ToResponseMarshallable((StatusCodes.Created, u)))
}
}
}
}
}

getting scheme java.lang.NullPointerException: scheme while trying to fake a server call in my service

I have a service class in my project and I want to test one of its methods that is performing an api call, so I want to catch this call and return something fake so I can test my method, it looks like this:
class MyService #Inject()(implicit config: Configuration, wsClient: WSClient) {
def methodToTest(list: List[String]): Future[Either[BadRequestResponse, Unit]] = {
wsClient.url(url).withHeaders(("Content-Type", "application/json")).post(write(list)).map { response =>
response.status match {
case Status.OK =>
Right(logger.debug("Everything is OK!"))
case Status.BAD_REQUEST =>
Left(parse(response.body).extract[BadRequestResponse])
case _ =>
val ex = new RuntimeException(s"Failed with status: ${response.status} body: ${response.body}")
logger.error(s"Service failed: ", ex)
throw ex
}
}
}
}
and now in my test class I go:
class MyServiceTest extends FreeSpec with ShouldMatchers with OneAppPerSuite with ScalaFutures with WsScalaTestClient {
implicit lazy val materializer: Materializer = app.materializer
lazy val config: Configuration = app.injector.instanceOf[Configuration]
lazy val myService = app.injector.instanceOf[MyService]
"My Service Tests" - {
"Should behave as im expecting" in {
Server.withRouter() {
case POST(p"/fake/api/in/conf") => Action { request =>
Results.Ok
}
} { implicit port =>
WsTestClient.withClient { implicit client =>
whenReady(myService.methodToTest(List("1","2","3"))) { res =>
res.isRight shouldBe true
}
}
}
}
}
}
and I get this error:
scheme java.lang.NullPointerException: scheme
also tried put under client => :
val myService = new MyService {
implicit val config: Configuration = configuration
implicit val ws: WSClient = client
}
but got some other error that I dont have enough arguments in the constructor...
why is it not working?
if there is a better nd simpler way to fake this api call i will love to hear it :)
thanks!
Server.withRouter may not be exactly what you want. It creates a server and bound it to a random port, per instance (unless you specify the port). It also creates its own instance of application which will be disconnected from the app you used to instantiate the service.
Another thing is that the injected WSClient do not works relative to your application. You need to use the client which is passed to WsTestClient.withClient block instead. So, you should do something like:
class MyServiceTest extends FreeSpec with ShouldMatchers with OneAppPerSuite with ScalaFutures with WsScalaTestClient {
implicit lazy val materializer: Materializer = app.materializer
lazy val config: Configuration = app.injector.instanceOf[Configuration]
"My Service Tests" - {
"Should behave as im expecting" in {
Server.withRouter() {
case POST(p"/fake/api/in/conf") => Action { request =>
Results.Ok
}
} { implicit port =>
WsTestClient.withClient { implicit client =>
// Use the client "instrumented" by Play. It will
// handle the relative aspect of the url.
val myService = new MyService(client, config)
whenReady(myService.methodToTest(List("1","2","3"))) { res =>
res.isRight shouldBe true
}
}
}
}
}
}

How to write tests for a Play Framework Filter?

I've written a Filter for my Play application:
object MyFilter {
def apply() = new MyFilter()
}
class MyFilter extends EssentialFilter {
def apply(next: EssentialAction) = new EssentialAction {
def apply(requestHeader: RequestHeader) = {
requestHeader.cookies.get("myCookie") match {
case Some(cookie) => {
requestHeader.session + ("importantValue", cookie.value)
next(requestHeader)
}
case None => {
val importantValue = ... // retrieve the value from somewhere
requestHeader.session + ("importantValue", importantValue)
next(requestHeader).map(_.withCookies(Cookie("importantValue", value)))
}
}
}
}
There're a lot of examples in the Play documentation about how to write test, but I've no idea how to unit test MyFilter. Can someone help me a little?
Thanks
Torben
The play framework source code shows test for filters.
for example:
val filter = SecurityHeadersFilter()
// Play.current is set at this point...
val rh = FakeRequest()
val action = Action(Ok("success"))
val result = filter(action)(rh).run()
header(X_FRAME_OPTIONS_HEADER, result) must beSome("DENY")
taken from here SecurityHeadersFilterSpec.scala
Not answering your question but requestHeader.session + ("importantValue", cookie.value) is never going to do anything. You're creating a new session object and don't do anything with it.

Scala Best Practices: Trait Inheritance vs Enumeration

I'm currently experimenting with Scala and looking for best practices. I found myself having two opposite approaches to solving a single problem. I'd like to know which is better and why, which is more conventional, and if maybe you know of some other better approaches. The second one looks prettier to me.
1. Enumeration-based solution
import org.squeryl.internals.DatabaseAdapter
import org.squeryl.adapters.{H2Adapter, MySQLAdapter, PostgreSqlAdapter}
import java.sql.Driver
object DBType extends Enumeration {
val MySql, PostgreSql, H2 = Value
def fromUrl(url: String) = {
url match {
case u if u.startsWith("jdbc:mysql:") => Some(MySql)
case u if u.startsWith("jdbc:postgresql:") => Some(PostgreSql)
case u if u.startsWith("jdbc:h2:") => Some(H2)
case _ => None
}
}
}
case class DBType(typ: DBType) {
lazy val driver: Driver = {
val name = typ match {
case DBType.MySql => "com.mysql.jdbc.Driver"
case DBType.PostgreSql => "org.postgresql.Driver"
case DBType.H2 => "org.h2.Driver"
}
Class.forName(name).newInstance().asInstanceOf[Driver]
}
lazy val adapter: DatabaseAdapter = {
typ match {
case DBType.MySql => new MySQLAdapter
case DBType.PostgreSql => new PostgreSqlAdapter
case DBType.H2 => new H2Adapter
}
}
}
2. Singleton-based solution
import org.squeryl.internals.DatabaseAdapter
import org.squeryl.adapters.{H2Adapter, MySQLAdapter, PostgreSqlAdapter}
import java.sql.Driver
trait DBType {
def driver: Driver
def adapter: DatabaseAdapter
}
object DBType {
object MySql extends DBType {
lazy val driver = Class.forName("com.mysql.jdbc.Driver").newInstance().asInstanceOf[Driver]
lazy val adapter = new MySQLAdapter
}
object PostgreSql extends DBType {
lazy val driver = Class.forName("org.postgresql.Driver").newInstance().asInstanceOf[Driver]
lazy val adapter = new PostgreSqlAdapter
}
object H2 extends DBType {
lazy val driver = Class.forName("org.h2.Driver").newInstance().asInstanceOf[Driver]
lazy val adapter = new H2Adapter
}
def fromUrl(url: String) = {
url match {
case u if u.startsWith("jdbc:mysql:") => Some(MySql)
case u if u.startsWith("jdbc:postgresql:") => Some(PostgreSql)
case u if u.startsWith("jdbc:h2:") => Some(H2)
case _ => None
}
}
}
If you declare a sealed trait DBType, you can pattern match on it with exhaustiveness checking (ie, Scala will tell you if you forget one case).
Anyway, I dislike Scala's Enumeration, and I'm hardly alone in that. I never use it, and if there's something for which enumeration is really the cleanest solution, it is better to just write it in Java, using Java's enumeration.
For this particular case you don't really need classes for each database type; it's just data. Unless the real case is dramatically more complex, I would use a map and string parsing based solution to minimize the amount of code duplication:
case class DBRecord(url: String, driver: String, adapter: () => DatabaseAdapter) {}
class DBType(record: DBRecord) {
lazy val driver = Class.forName(record.driver).newInstance().asInstanceOf[Driver]
lazy val adapter = record.adapter()
}
object DBType {
val knownDB = List(
DBRecord("mysql", "com.mysql.jdbc.Driver", () => new MySQLAdapter),
DBRecord("postgresql", "org.postgresql.Driver", () => new PostgreSqlAdapter),
DBRecord("h2", "org.h2.Driver", () => new H2Adapter)
)
val urlLookup = knownDB.map(rec => rec.url -> rec).toMap
def fromURL(url: String) = {
val parts = url.split(':')
if (parts.length < 3 || parts(0) != "jdbc") None
else urlLookup.get(parts(1)).map(rec => new DBType(rec))
}
}
I'd go for the singleton variant, since it allows clearer subclassing.
Also you might need to do db-specific things/overrides, since some queries/subqueries/operators might be different.
But i'd try something like this:
import org.squeryl.internals.DatabaseAdapter
import org.squeryl.adapters.{H2Adapter, MySQLAdapter, PostgreSqlAdapter}
import java.sql.Driver
abstract class DBType(jdbcDriver: String) {
lazy val driver = Class.forName(jdbcDriver).newInstance().asInstanceOf[Driver]
def adapter: DatabaseAdapter
}
object DBType {
object MySql extends DBType("com.mysql.jdbc.Driver") {
lazy val adapter = new MySQLAdapter
}
object PostgreSql extends DBType("org.postgresql.Driver") {
lazy val adapter = new PostgreSqlAdapter
}
object H2 extends DBType("org.h2.Driver") {
lazy val adapter = new H2Adapter
}
def fromUrl(url: String) = {
url match {
case _ if url.startsWith("jdbc:mysql:") => Some(MySql(url))
case _ if url.startsWith("jdbc:postgresql:") => Some(PostgreSql(url))
case _ if url.startsWith("jdbc:h2:") => Some(H2(url))
case _ => None
}
}
if this helped, please consider to +1 this :)