De/Serializing LocalDateTime with akka-http - scala

I am trying to add support for serializing and deserializing LocalDateTime in akka-http. I'm a little confused by the following error:
Error:(42, 20) could not find implicit value for parameter um:
akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[SomeData]
entity(as[SomeData]) { evt => complete(StatusCodes.Created) }
Error:(42, 20) not enough arguments for method as: (implicit um:
akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[SomeData])akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[SomeData].
Unspecified value parameter um.
entity(as[SomeData]) { evt => complete(StatusCodes.Created) }
What an I missing in the following code? I pasted my code into the minimalist sample from the Akka-http documentation. I am running Akka-http 10.0.6.
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.io.StdIn
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import spray.json.{DefaultJsonProtocol, JsString, JsValue, JsonFormat}
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.server.Route
final case class SomeData (dateTime: LocalDateTime)
trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
implicit val eventDataFormat: JsonFormat[SomeData] = jsonFormat1(SomeData)
implicit val localDateTimeFormat = new JsonFormat[LocalDateTime] {
private val iso_date_time = DateTimeFormatter.ISO_DATE_TIME
def write(x: LocalDateTime) = JsString(iso_date_time.format(x))
def read(value: JsValue) = value match {
case JsString(x) => LocalDateTime.parse(x, iso_date_time)
case x => throw new RuntimeException(s"Unexpected type ${x.getClass.getName} when trying to parse LocalDateTime")
}
}
// implicit val eventDataFormat: JsonFormat[SomeData] = jsonFormat1(SomeData) ** moved here **
}
object Main extends JsonSupport {
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
val route:Route =
path("hello") {
post {
entity(as[SomeData]) { evt => complete(StatusCodes.Created) }
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
StdIn.readLine() // let it run until user presses return
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
}
}

implicit val eventDataFormat: JsonFormat[SomeData] = jsonFormat1(SomeData)
↓
implicit val eventDataFormat = jsonFormat1(SomeData)

Related

Put elements in stream and return an object

In akka, I want to put the elements in stream and return an object. I know the elements could be a source to run a graph. But how can I put the element and return an object on runtime?
import akka.actor.ActorSystem
import akka.stream.QueueOfferResult.{Dropped, Enqueued, Failure, QueueClosed}
import akka.stream.{ActorMaterializer, OverflowStrategy}
import akka.stream.scaladsl.{Keep, Sink, Source}
import scala.Array.range
import scala.util.Success
object StreamElement {
implicit val system = ActorSystem("StreamElement")
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
def main(args: Array[String]): Unit = {
val (queue, value) = Source
.queue[Int](10, OverflowStrategy.backpressure)
.map(x => {
x * x
})
.toMat(Sink.asPublisher(false))(Keep.both)
.run()
range(0, 10)
.map(x => {
queue.offer(x).onComplete {
case Success(Enqueued) => {
}
case Success(Dropped) => {}
case _ => {
println("others")
}
}
})
}
}
How can I get the value returned?
Actually, you want to return the int value for each element.
So you could create the flow, then connect to source and Sink for each time.
package tech.parasol.scala.akka
import akka.actor.ActorSystem
import akka.stream.QueueOfferResult.{Dropped, Enqueued, Failure, QueueClosed}
import akka.stream.{ActorMaterializer, OverflowStrategy}
import akka.stream.scaladsl.{Flow, Keep, Sink, Source}
import scala.Array.range
import scala.util.Success
object StreamElement {
implicit val system = ActorSystem("StreamElement")
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
val flow = Flow[Int]
.buffer(16, OverflowStrategy.backpressure)
.map(x => x * x)
def main(args: Array[String]): Unit = {
range(0, 10)
.map(x => {
Source.single(x).via(flow).runWith(Sink.head)
}.map( v => println("v ===> " + v)
))
}
}
It's unclear to me why the Scala collection isn't fed to the Stream as a Source in your sample code. Given that you've already composed a Stream with materialized values to be captured in a Source Queue and a publisher Sink, you could create a subscriber Source using Source.fromPublisher to collect the wanted values, as shown below:
import akka.actor.ActorSystem
import akka.stream.scaladsl._
import akka.stream._
implicit val system = ActorSystem("system")
implicit val materializer = ActorMaterializer() // Not needed for Akka 2.6+
val (queue, pub) = Source
.queue[Int](10, OverflowStrategy.backpressure)
.map(x => x * x)
.toMat(Sink.asPublisher(false))(Keep.both)
.run()
val fromQueue = Source(0 until 10).runForeach(queue.offer(_))
val source = Source.fromPublisher(pub)
source.runForeach(x => print(x + " "))
// Output:
// 0 1 4 9 16 25 36 49 64 81

akka http compile error

I am new in akka framework and now try to setup simple webservice with this framework .
write a simple akka-http application :
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.io.StdIn
object MainRunner extends App {
implicit val system = ActorSystem("mySystem")
implicit val materializer = ActorMaterializer
implicit val ec = system.dispatcher
val route =
path("hello") {
get {
complete("Congratulation , this is your response")
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
StdIn.readLine() // let it run until user presses return
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
}
receive this error on compile :
Error:(34, 44) type mismatch;
found : akka.http.scaladsl.server.Route
(which expands to) akka.http.scaladsl.server.RequestContext => scala.concurrent.Future[akka.http.scaladsl.server.RouteResult]
required: akka.stream.scaladsl.Flow[akka.http.scaladsl.model.HttpRequest,akka.http.scaladsl.model.HttpResponse,Any]
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
How can fix that ?
It is just a simple mistake when instantiating your ActorMaterializer:
implicit val materializer = ActorMaterializer
should be replaced by
implicit val materializer = ActorMaterializer()
With a valid materializer in scope, the implicit conversion between the Route and the Flow[HttpRequest, HttpResponse, _] should happen as expected, and the compiler should be happy.

How to check if Scala Spray Bind actually binds to port

From the Scala Spray documentation it is not clear how to check if it is not able to bind to a particular port
implicit val system = ActorSystem("mediaiton")
implicit val timeout = Timeout(5, TimeUnit.SECONDS)
val service = system.actorOf(Props[IotRestNB], "mediaiton")
println(s"Going to start the REST NB at $host $port")
IO(Http) ! Http.Bind(service, interface = host, port = port)
Spend a day trying to figure out from different post
import java.util.concurrent.TimeUnit
import akka.actor.{ActorSystem, Props}
import akka.io.IO
import akka.util.Timeout
import nb.rest.IotRestNB
import spray.can.Http
implicit val system = ActorSystem("lwm2m-mediaiton")
implicit val timeout = Timeout(5, TimeUnit.SECONDS)
val service = system.actorOf(Props[IotClassNB], "lwm2m-mediaiton")
println(s"Going to start the REST NB at $host $port")
IO(Http).tell(Http.Bind(service, interface = host, port = port), sender = service)
Now the actor Class - IotClassNB
import java.util.concurrent.Executors
import akka.actor.Actor
import lwm2m.server.BootUp
import org.eclipse.leshan.core.request.ContentFormat
import spray.can.Http._
import spray.http.MediaTypes
import spray.routing.HttpService
import scala.concurrent.{ExecutionContext, Future}
class IotClassNBextends Actor with MediationRoute {
//mixin class
def actorRefFactory = context
def receive = handleConnection orElse runRoute(route)
def handleConnection: Receive = {
case b: Bound =>
println("***REST Server Started***")
Future.successful(b)
case failed: CommandFailed =>
println("***REST Server Could not be Started***")//this is what we want
Future.failed(new
RuntimeException("Binding failed"))
}
}
trait MediationRoute extends HttpService {
// Execution Context for blocking ops
val blockingExecutionContext = {
ExecutionContext.fromExecutor(Executors.newFixedThreadPool(10))
}
val route = {
pathPrefix("v1") {
pathPrefix("mediation") {
respondWithMediaType(MediaTypes.`application/json`) {
pathPrefix("get_connected_clients") {
pathEnd {
get {
complete(
// Future.apply {
get_registered_clients())
// }(blockingExecutionContext))
}
}.....
And here is how to test your Spary server via Spray Client
#Test def test_RestNB(): Unit = {
implicit val system = ActorSystem("test")
import system.dispatcher
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
implicit val timeout = Timeout(25, TimeUnit.SECONDS)
val server_url = s"http://${host}:${port}/xxx/"
val response: Future[HttpResponse] = pipeline(Get(server_url))
val result = Await.result(response, timeout.duration) //wait for timeout
// println(s"Await Result is ${result.entity.asString}")
response.onComplete {
case Success(result: HttpResponse) =>
logger.info("Result: " + result.entity.asString)
assert(result.entity.asString === xxxxx")
case Failure(error) =>
logger.error(error + "Couldn't get list of items")
case _ =>
assert(false)
}

akka-http send continuous chunked http response (stream)

I have this crude test example with akka-http client and server.
Server.scala:
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Sink
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model._
import scala.concurrent.Future
class Server extends Runnable {
def run() = {
implicit val system = ActorSystem("server")
implicit val materializer = ActorMaterializer()
val serverSource = Http().bind(interface = "localhost", port = 8200)
val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(GET, Uri.Path("/stream"), _, _, _) =>
HttpResponse(entity = HttpEntity(MediaTypes.`text/plain`, "test"))
}
val bindingFuture: Future[Http.ServerBinding] = serverSource.to(Sink.foreach { connection =>
connection handleWithSyncHandler requestHandler
}).run()
}
}
Client.scala:
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{Uri, HttpRequest}
import akka.stream.ActorMaterializer
object Client extends App {
implicit val system = ActorSystem("client")
import system.dispatcher
new Thread(new Server).start()
implicit val materializer = ActorMaterializer()
val source = Uri("http://localhost:8200/stream")
val finished = Http().singleRequest(HttpRequest(uri = source)).flatMap { response =>
response.entity.dataBytes.runForeach { chunk =>
println(chunk.utf8String)
}
}
}
At the moment Server just replies with a single "test".
How do I change the HttpResponse in Server to send "test" as chunked (stream) in an endless loop every 1 second?
Found the answer.
Server.scala:
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Source, Sink}
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model._
import scala.concurrent.Future
import scala.concurrent.duration._
class Server extends Runnable {
def run() = {
implicit val system = ActorSystem("server")
implicit val materializer = ActorMaterializer()
val serverSource = Http().bind(interface = "localhost", port = 8200)
val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(GET, Uri.Path("/stream"), _, _, _) =>
HttpResponse(entity = HttpEntity.Chunked(ContentTypes.`text/plain`, Source(0 seconds, 1 seconds, "test")))
}
val bindingFuture: Future[Http.ServerBinding] = serverSource.to(Sink.foreach { connection =>
connection handleWithSyncHandler requestHandler
}).run()
}
}

Where to define the implicit val formats in a microservice with Scala, akka-http, Json4s?

I am building a microservice using Scala, akka-http, json4s. Also using case classes for my business bean classes. My case classes have scala Enumerations (I researched on the scala Enums and aware of the limitations, but this perfectly suits my current use cases).
With this background, when I tried to create a service, I am not able to understand where to define the
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
Following is my rough scala class structure:
import akka.actor.ActorSystem
import akka.event.{Logging, LoggingAdapter}
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshalling.ToResponseMarshallable
import akka.http.scaladsl.model.StatusCodes._
import akka.http.scaladsl.server.Directives
import akka.stream.ActorMaterializer
import com.typesafe.config.{Config, ConfigFactory}
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import gremlin.scala.ScalaVertex
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex
import org.json4s.ext.EnumNameSerializer
import org.json4s.{DefaultFormats, jackson}
import scala.concurrent.{ExecutionContext, ExecutionContextExecutor, Future}
trait Service {
implicit val system: ActorSystem
implicit def executor: ExecutionContextExecutor
implicit val materializer: ActorMaterializer
// ?? implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
lazy val client = TitanConnection.cluster.connect();
def config: Config
val logger: LoggingAdapter
def addPerson(p: Person): Future[Either[String, Person]] = {
try {
//Code to add person to database
val resultPerson = Person(propertyMap)
Future.successful(Right(resultPerson))
} catch {
case e :Exception => e.printStackTrace
Future.failed(new Exception("Person can't be created"))
}
}
def fetchPerson(pId: String): Future[Either[Error, Person]] = {
try {
//Code to fetch person object from database
result = results.one() //fetches the first record from results, if it exists
//Following is code for validation of the data
if(result.isNull)
Future.successful(Left(new Error("FAILED","","",s"""There is no person with requested personID:$pId""")))
else {
//Code to retrieve person and return the same as object
Future.successful(Right(resultPerson))
}
} catch {
case e :Exception => e.printStackTrace
Future.successful(Left(new Error("FATAL","","",s"""There is some exception while retrieving person with requested personID:$pId""")))
}
}
/** This is the list of operations possible on the person business entity.
*
* #param ec
* #return
*/
def routes(implicit ec: ExecutionContext) = {
import Directives._
import Json4sSupport._
implicit val serialization = jackson.Serialization // or native.Serialization
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
logRequestResult("PersonMicroservice") {
pathPrefix("person") {
(get & path(Segment)) { personId =>
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
complete {
fetchPerson(personId).map[ToResponseMarshallable] {
case Right(personFormat) => personFormat
case Left(errorMessage) => BadRequest -> errorMessage
}
}
}~
post { entity(as[Person]) { entity =>
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
complete {
addPerson(entity).map[ToResponseMarshallable] {
case Right(personFormat) => personFormat
case Left(error) => BadRequest -> error
}
}
}
}
}
}
}
}
/** Microservice for "Person" business entity. This microservice shall handle the basic CRUD related operations
* of Person business entity.
*/
object PersonMicroService extends App with Service {
override implicit val system = ActorSystem()
override implicit val executor = system.dispatcher
override implicit val materializer = ActorMaterializer()
override val config = ConfigFactory.load()
override val logger = Logging(system, getClass)
Http().bindAndHandle(routes , config.getString("http.interface"), config.getInt("http.port"))
}
I also have a ScalaTest spec for unit testing the service, and I am also forced to define the formats in each of the test case. Not sure if am doing this right. Hence seeking expert advice.
Following is my test spec:
package in.niftyride.unit
import akka.event.{Logging, LoggingAdapter}
import akka.http.scaladsl.server.Directives
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import org.json4s.{jackson, DefaultFormats}
import org.json4s.ext.EnumNameSerializer
import akka.http.scaladsl.model.ContentTypes._
import akka.http.scaladsl.model.StatusCodes._
import Json4sSupport._
class PersonEndpointSpec extends UnitServiceSpec{
override def testConfigSource = "akka.loglevel = WARNING"
override def config = testConfig
override lazy val client = TestDatabaseProvider.cluster.connect;
val logger: LoggingAdapter = Logging(system, this.getClass)
System.setProperty(DatabaseUtils.SERVER_HASH_TEXT, DatabaseUtils.RANDOM_HASH)
"Service" should "respond to single id query" in {
implicit val serialization = jackson.Serialization // or native.Serialization
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
Get(s"/person/${PersonTestData.personId1}") ~> routes ~> check {
status shouldBe OK
contentType shouldBe `application/json`
responseAs[Person] shouldBe PersonTestData.minData1
}
}
it should "be possible to create a person with valid data through POST" in {
implicit val serialization = jackson.Serialization // or native.Serialization
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
Post(s"/person", PersonTestData.minDataEmptyPersonId1) ~> routes ~> check {
status shouldBe OK
contentType shouldBe `application/json`
responseAs[Person] shouldBe PersonTestData.minData1
}
}
}