Scala, Sangria and Scalatra - scala

We have a Scala application using Scalatra (http://scalatra.org/) as our web framework. I'm wondering if there are any good (or just any) resources out there on how to implement a GraphQL endpoint using Sangria (http://sangria-graphql.org/) and Scalatra?
I'm new to Scala and would appreciate any help to get started on this.

There aren't any that I know of but since Scalatra uses json4s you would use sangria's json4s marshaller .
Otherwise, if sangria could be clearer to you, here's a scala worksheet with a very simplistic example based off play + sangria - in this case you would just need to swap the json library.
The db is mocked (perhaps you use Slick?) and the http server as well but it's a simple case of swapping in the function definitions.
import sangria.ast.Document
import sangria.execution.{ErrorWithResolver, Executor, QueryAnalysisError}
import sangria.macros.derive.{ObjectTypeDescription, ObjectTypeName, deriveObjectType}
import sangria.parser.{QueryParser, SyntaxError}
import sangria.renderer.SchemaRenderer
import sangria.schema.{Argument, Field, IntType, ListType, ObjectType, OptionInputType, Schema, fields}
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success}
// replace with another json lib
// eg https://github.com/sangria-graphql/sangria-json4s-jackson
import play.api.libs.json._
import sangria.marshalling.playJson._
case class User(name: String, age: Int, phone: Option[String])
class FakeDb {
class UsersTable {
def getUsers(limit: Int): List[User] = {
// this would come from the db
List(
User("john smith", 23, None),
User("Anne Schwazenbach", 45, Some("2134556"))
)
}
}
val usersRepo = new UsersTable
}
object MySchema {
val limitArg: Argument[Int] = Argument("first", OptionInputType(IntType),
description = s"Returns the first n elements from the list.",
defaultValue = 10)
implicit val UsersType: ObjectType[FakeDb, User] = {
deriveObjectType[FakeDb, User](
ObjectTypeName("Users"),
ObjectTypeDescription("Users in the system")
)
}
private val Query: ObjectType[FakeDb, Unit] = ObjectType[FakeDb, Unit](
"Query", fields[FakeDb, Unit](
Field("users", ListType(UsersType),
arguments = limitArg :: Nil,
resolve = c => c.ctx.usersRepo.getUsers(c.arg(limitArg))
)
))
val theSchema: Schema[FakeDb, Unit] = Schema(Query)
}
object HttpServer {
def get(): String = {
// Http GET
SchemaRenderer.renderSchema(MySchema.theSchema)
}
def post(query: String): Future[JsValue] = {
// Http POST
val variables = None
val operation = None
QueryParser.parse(query) match {
case Success(q) => executeQuery(q, variables, operation)
case Failure(error: SyntaxError) => Future.successful(Json.obj("error" -> error.getMessage))
case Failure(error: Throwable) => Future.successful(Json.obj("error" -> error.getMessage))
}
}
private def executeQuery(queryAst: Document, vars: Option[JsValue], operation: Option[String]): Future[JsValue] = {
val schema: Schema[FakeDb, Unit] = MySchema.theSchema
Executor.execute[FakeDb, Unit, JsValue](schema, queryAst, new FakeDb,
operationName = operation,
variables=vars.getOrElse(Json.obj()))
.map((d: JsValue) => d)
.recover {
case error: QueryAnalysisError ⇒ Json.obj("error" -> error.getMessage)
case error: ErrorWithResolver ⇒ Json.obj("error" -> error.getMessage)
}
}
}
HttpServer.get()
val myquery = """
{
users {
name
}
}
"""
val res: JsValue = Await.result(HttpServer.post(myquery), 10.seconds)

Related

Slick Futures converted into Promises

I am starting to develop in Scala, so I started witha really simple RESTful API using AKKA HTTP actors and then wanted to add a PostgreSQL database to "close up" the project. The thing is that somewhere in the project, a Future that is returned by a db.run method is converted into a Promise and returning me errors. When I run the Main object and start the API and hit somewhere, I get this error:
Cannot cast scala.concurrent.impl.Promise$DefaultPromise to scala.collection.immutable.Seq or Cannot cast scala.concurrent.impl.Promise$DefaultPromise to api.Item depending on which route I an hitting.
Here is the main api.scala file:
package api
import akka.actor.{Actor, ActorSystem, Props}
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.pattern.ask
import akka.util.Timeout
import api.Main.ItemActor._
import slick.jdbc.JdbcBackend.Database
import spray.json.DefaultJsonProtocol._
import scala.concurrent.Future
import scala.concurrent.duration.DurationInt
import scala.util.{Failure, Success}
object Main extends App {
val db = Database.forConfig("scaladb");
val itemDao = new handler(db)
val system = ActorSystem("mySystem")
val itemActor = system.actorOf(Props(new ItemActor(db)))
implicit val actorSystem = system
implicit val itemFormat = jsonFormat3(Item)
implicit val timeout: Timeout = Timeout(5.seconds)
class ItemActor(db: Database) extends Actor {
import api.Main.ItemActor._
def receive = {
case CreateItem(item) =>
sender() ! itemDao.create(item)
case ReadItem(id) =>
sender() ! itemDao.read(id)
case ReadAllItems =>
sender() ! itemDao.readAll
case UpdateItem(item) =>
sender() ! itemDao.update(item)
case DeleteItem(id) =>
sender() ! itemDao.delete(id)
}
}
object ItemActor {
case class CreateItem(item: Item)
case class ReadItem(id: Int)
case object ReadAllItems
case class UpdateItem(item: Item)
case class DeleteItem(id: Int)
}
def handleResponse(futureResponse: Future[Item]): Route = {
onComplete(futureResponse) {
case Success(response) => complete(response)
case Failure(ex) => complete(StatusCodes.InternalServerError, s"An error occurred: ${ex.getMessage}")
}
}
def handleResponseSeq(futureResponse: Future[Seq[Item]]): Route = {
onComplete(futureResponse) {
case Success(response) => complete(response)
case Failure(ex) => complete(StatusCodes.InternalServerError, s"An error occurred: ${ex.getMessage}")
}
}
val routes = pathPrefix("items") {
pathEnd {
post {
entity(as[Item]) { item =>
handleResponse((itemActor ? CreateItem(item)).mapTo[Item])
}
} ~
get {
handleResponseSeq((itemActor ? ReadAllItems).mapTo[Seq[Item]])
}
} ~
path(IntNumber) { id =>
get {
handleResponse((itemActor ? ReadItem(id)).mapTo[Item])
} ~
put {
entity(as[Item]) { item =>
handleResponse((itemActor ? UpdateItem(item)).mapTo[Item])
}
} ~
delete {
handleResponse((itemActor ? DeleteItem(id)).mapTo[Item])
}
}
}
val bindRoutes = Http().bindAndHandle(routes, "localhost", 8888)
println("Server online at http://localhost:8888/")
}
Then the handler (Where I definde the methods that access the PostgreSQL database):
package api
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.Future
class handler (db:Database){
val items = TableQuery[Items]
def create(item:Item): Future[Item] = {
db.run((items returning items.map(_.id.?) into ((item, id) => item.copy(id = id))) += item)
}
def read(id: Int): Future[Option[Item]] = {
db.run(items.filter(_.id === id).result.headOption)
}
def readAll: Future[Seq[Item]] = {
println((db.run(items.result)).getClass)
db.run(items.result)
}
def update(item: Item): Future[Int] = {
db.run(items.filter(_.id === item.id).update(item))
}
def delete(id: Int): Future[Int] = {
db.run(items.filter(_.id === id).delete)
}
}
And the items file:
package api
import slick.jdbc.PostgresProfile.api._
case class Item(id: Option[Int] = None, name: String, description: String)
class Items(tag: Tag) extends Table[Item](tag, "items") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def description = column[String]("description")
def * = (id.?, name, description) <> (Item.tupled, Item.unapply)
}
I've tried to use a getClass next to the db.run(items.result) in the handler file, and it prits class scala.concurrent.impl.Promise$DefaultPromise so it must be something of an implicit converter. Thanks.
You're mixing Futures and actors, which is generally not a great idea.
In your ItemActor, instead of sending the future as a reply, it's a better idea to pipe the future as a reply (the reply won't actually happen until the future is complete, that is to say, the DAO has a result).
import akka.pattern.pipe
class ItemActor(db: Database) extends Actor {
import ItemActor._
import context.dispatcher
def receive = {
case CreateItem(item) =>
itemDao.create(item).pipeTo(sender())
case ReadItem(id) =>
itemDao.read(id).pipeTo(sender())
}
}
That said, at least in this code, there doesn't really seem to be a good reason for ItemActor to exist, given that it's just forwarding operations to the DAO. Making the itemDao visible in the routes, you could just as well do:
handleResponse(itemDao.create(item))
Here: handleResponse((itemActor ? CreateItem(item)).mapTo[Item])
Actor returns Future[Item], mapTo[Item] tries to cast it to item and fails.
You want your actor to return the actual item, not Future result from db.run.
I haven't used akka in a while, but I think, something like this should work:
val replyTo = sender
...
case CreateItem(item) => itemDao.create(item).onComplete {
case Success(i) => replyTo ! i
case Failure(e) => throw e
}
...

How to write Junit test cases for ZIO Http?

Here's my code:
import zhttp.http._
import zio._
import zhttp.http.{Http, Method, Request, Response, Status}
import zhttp.service.Server
case class Experiments(experimentId: String, variantId: String, accountId: String, deviceId: String, date: Int)
object Experiments {
implicit val encoder: JsonEncoder[Experiments] = DeriveJsonEncoder.gen[Experiments]
implicit val decoder: JsonDecoder[Experiments] = DeriveJsonDecoder.gen[Experiments]
implicit val codec: JsonCodec[Experiments] = DeriveJsonCodec.gen[Experiments]
implicit val schema: Schema[Experiments] = DeriveSchema.gen
}
object HttpService {
def apply(): Http[ExpEnvironment, Throwable, Request, Response] =
Http.collectZIO[Request] {
case req#(Method.POST -> !! / "zioCollector") =>
val c = req.body.asString.map(_.fromJson[Experiments])
for {
u <- req.body.asString.map(_.fromJson[Experiments])
r <- u match {
case Left(e) =>
ZIO.debug(s"Failed to parse the input: $e").as(
Response.text(e).setStatus(Status.BadRequest)
)
case Right(u) =>
println(s"$u + =====")
ExpEnvironment.register(u)
.map(id => Response.text(id))
}
}
yield r
}
}
object ZioCollectorMain extends ZIOAppDefault {
def run: ZIO[Environment with ZIOAppArgs with Scope, Any, Any] = {
Server.start(
port = 9001,
http = HttpService()).provide(ZLayerExp.layer)
}
}
How to write Junit test cases for ZIO Http?
I'm looking into Zio-testing, hopefully this would be helpful.
Any docs, links, code is helpful
#Zio # Zio-http #Zio-json #Scala #AkkatoZioMigration

slick filter from any other class

I have a case class, mappings and the object defined like this
import slick.driver.PostgresDriver.api._
import scala.concurrent.Await
import scala.concurrent.duration.Duration
case class Coffee(name : String, supID: Int, price: Double, sales: Int, total: Int) {
def save(): Coffee = { Coffees.save(this) }
def delete() { Coffees.delete(this) }
}
class Coffees(tag: Tag) extends Table[Coffee](tag, "coffee") {
def name = column[String]("cof_name", O.PrimaryKey)
def supID = column[Int]("sup_id")
def price = column[Double]("price")
def sales = column[Int]("sales", O.Default(0))
def total = column[Int]("total", O.Default(0))
def * = (name, supID, price, sales, total) <> (Coffee.tupled, Coffee.unapply)
}
object Coffees extends TableQuery(new Coffees(_)) {
lazy val db = DatabaseAccess.databases("db.test")
def save(coffee: Coffee): Coffee = {
val saveQuery = (this returning this).insertOrUpdate(coffee)
Await.result(db.run(saveQuery), Duration.Inf) match {
case Some(x) => x
case None => coffee
}
}
def delete(coffee: Coffee) = {
Await.result(db.run(this.filter(_.name === coffee.name).delete), Duration.Inf)
}
}
Now I want to write queries from other classes like this :
import com.typesafe.config.ConfigFactory
import org.scalatest.{FeatureSpec, GivenWhenThen, Matchers}
class CoffeeSpec extends FeatureSpec with GivenWhenThen with Matchers {
feature("Accessing coffees in the database") {
scenario("Adding a new coffee") {
val config = ConfigFactory.load()
DatabaseAccess.loadConfiguration(config)
lazy val db = DatabaseAccess.databases("db.test")
val coffee = Coffee("nescafe", 1, 20, 20, 40).save()
val temp = Coffees.filter(_.name === "nescafe")
temp.length should be (1)
coffee.delete()
}
}
}
This line
val temp = Coffees.filter(_.name === "nescafe")
Throws an error like this:
What is the best way to write the filter queries on the Coffees object?
Using slick 2.0 I have queries like:
Query(Coffees).filter(_.name is "coffee").firstOption.getOrElse(None)
I want to be able to do the similar queries using the new setup for all the data mappings.
Why am I getting these errors and how should I be able to make similar queries in slick 3?
Your queries are fine, but your CoffeeSpec file is missing an import:
import slick.driver.PostgresDriver.api._
Compilation should pass then.

Get whole HttpResponse body as a String with Akka-Streams HTTP

I'm trying to understand how to use the new akka.http library. I would like to send an http request to a server and read the whole response body as a single String in order to produce a Source[String,?].
Here is the best solution I was able to produce so far:
def get(
modelID: String,
pool: Flow[(HttpRequest,Int),(Try[HttpResponse],Int),Http.HostConnectionPool]
): Source[String,Unit] = {
val uri = reactionsURL(modelID)
val req = HttpRequest(uri = uri)
Source.single( (req,0) )
.via( pool )
.map {
case (Success(resp),_) =>
resp.entity.dataBytes.map( _.decodeString("utf-8") )
}.flatten(FlattenStrategy.concat)
.grouped( 1024 )
.map( _.mkString )
It seems to work well (except the missing error path), but it is a bit clunky for such simple tasks. Is there a smarter solution ? Can I avoid the grouped/mkString ?
You can use toStrict method of HttpResponse with timeout. It gathers whole answer as Future.
def toStrict(timeout: FiniteDuration)(implicit ec: ExecutionContext, fm: Materializer): Future[Strict] Returns a sharable and serializable
copy of this message with a strict entity.
Example:
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpResponse, HttpRequest}
import akka.stream.{Materializer, ActorMaterializer}
import akka.stream.scaladsl.{Sink, Flow, Source}
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.duration._
import scala.util.{Try, Success}
object Main extends App {
implicit val system = ActorSystem()
import system.dispatcher
implicit val materializer = ActorMaterializer()
val host = "127.0.0.1"
lazy val pool = Http().newHostConnectionPool[Int](host, 9000)
FlowBuilder.get("/path", pool).to(Sink.foreach(_.foreach(println))).run()
}
object FlowBuilder {
def get(modelID: String, pool: Flow[(HttpRequest, Int), (Try[HttpResponse], Int), Http.HostConnectionPool])
(implicit ec: ExecutionContext, mat: Materializer): Source[Future[String], Unit] = {
val uri = modelID
val req = HttpRequest(uri = modelID)
Source.single((req, 0)).via(pool)
.map {
case (Success(resp), _) => resp.entity.toStrict(5 seconds).map(_.data.decodeString("UTF-8"))
}
}
}
You can use Unmarshall which will also work on other types e.g. json from spray-json. This also as strict returns Future[_].
Example:
authedReq.via(authServerReqResFlow).mapAsync(1) { case (tryRes, _) =>
tryRes match {
case Failure(exception) => Future.failed[Principal](exception)
case Success(response # HttpResponse(StatusCodes.OK,_,_,_)) =>
val userContext = Unmarshal(response).to[UserContextData]
userContext.map {
case UserContextData(UserInfo(_, userName, fullName, email, title), _, _) =>
Principal(userName, fullName, email, title)
}
case Success(response # HttpResponse(responseCode,_,entity,_)) =>
Unmarshal(entity).to[String].flatMap(msg => Future.failed(new AuthenticationFailure(s"$responseCode\n$msg")))
}
}

Scala macro and type erasure

I'm having some problems with a macro I've written to help me log metrics represented as case class instances to to InfluxDB. I presume I'm having a type erasure problem and that the tyep parameter T is getting lost, but I'm not entirely sure what's going on. (This is also my first exposure to Scala macros.)
import scala.language.experimental.macros
import play.api.libs.json.{JsNumber, JsString, JsObject, JsArray}
abstract class Metric[T] {
def series: String
def jsFields: JsArray = macro MetricsMacros.jsFields[T]
def jsValues: JsArray = macro MetricsMacros.jsValues[T]
}
object Metrics {
case class LoggedMetric(timestamp: Long, series: String, fields: JsArray, values: JsArray)
case object Kick
def log[T](metric: Metric[T]): Unit = {
println(LoggedMetric(
System.currentTimeMillis,
metric.series,
metric.jsFields,
metric.jsValues
))
}
}
And here's an example metric case class:
case class SessionCountMetric(a: Int, b: String) extends Metric[SessionCountMetric] {
val series = "sessioncount"
}
Here's what happens when I try to log it:
scala> val m = SessionCountMetric(1, "a")
m: com.confabulous.deva.SessionCountMetric = SessionCountMetric(1,a)
scala> Metrics.log(m)
LoggedMetric(1411450638296,sessioncount,[],[])
Even though the macro itself seems to work fine:
scala> m.jsFields
res1: play.api.libs.json.JsArray = ["a","b"]
scala> m.jsValues
res2: play.api.libs.json.JsArray = [1,"a"]
Here's the actual macro itself:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
object MetricsMacros {
private def fieldNames[T: c.WeakTypeTag](c: Context)= {
val tpe = c.weakTypeOf[T]
tpe.decls.collect {
case field if field.isMethod && field.asMethod.isCaseAccessor => field.asTerm.name
}
}
def jsFields[T: c.WeakTypeTag](c: Context) = {
import c.universe._
val names = fieldNames[T](c)
Apply(
q"play.api.libs.json.Json.arr",
names.map(name => Literal(Constant(name.toString))).toList
)
}
def jsValues[T: c.WeakTypeTag](c: Context) = {
import c.universe._
val names = fieldNames[T](c)
Apply(
q"play.api.libs.json.Json.arr",
names.map(name => q"${c.prefix.tree}.$name").toList
)
}
}
Update
I tried Eugene's second suggestion like this:
abstract class Metric[T] {
def series: String
}
trait MetricSerializer[T] {
def fields: Seq[String]
def values(metric: T): Seq[Any]
}
object MetricSerializer {
implicit def materializeSerializer[T]: MetricSerializer[T] = macro MetricsMacros.materializeSerializer[T]
}
object Metrics {
def log[T: MetricSerializer](metric: T): Unit = {
val serializer = implicitly[MetricSerializer[T]]
println(serializer.fields)
println(serializer.values(metric))
}
}
with the macro now looking like this:
object MetricsMacros {
def materializeSerializer[T: c.WeakTypeTag](c: Context) = {
import c.universe._
val tpe = c.weakTypeOf[T]
val names = tpe.decls.collect {
case field if field.isMethod && field.asMethod.isCaseAccessor => field.asTerm.name
}
val fields = Apply(
q"Seq",
names.map(name => Literal(Constant(name.toString))).toList
)
val values = Apply(
q"Seq",
names.map(name => q"metric.$name").toList
)
q"""
new MetricSerializer[$tpe] {
def fields = $fields
def values(metric: Metric[$tpe]) = $values
}
"""
}
}
However, when I call Metrics.log -- specifically when it calls implicitly[MetricSerializer[T]] I get the following error:
error: value a is not a member of com.confabulous.deva.Metric[com.confabulous.deva.SessionCountMetric]
Why is it trying to use Metric[com.confabulous.deva.SessionCountMetric] instead of SessionCountMetric?
Conclusion
Fixed it.
def values(metric: Metric[$tpe]) = $values
should have been
def values(metric: $tpe) = $values
You're in a situation that's very close to one described in a recent question: scala macros: defer type inference.
As things stand right now, you'll have to turn log into a macro. An alternative would also to turn Metric.jsFields and Metric.jsValues into JsFieldable and JsValuable type classes materialized by implicit macros at callsites of log (http://docs.scala-lang.org/overviews/macros/implicits.html).