Akka Http: Calling TypedActor.context outside of a TypedActor implementation method - scala

I'm trying to build akka http client, by using this example Test akka http client
This is my code:
trait HttpServer {
def sendAndReceive: HttpRequest => Future[HttpResponse]
}
trait ApiHandler extends HttpServer {
implicit def as: ActorSystem
override def sendAndReceive: HttpRequest => Future[HttpResponse] =
Http().singleRequest(_)
}
trait Api {
this: ApiHandler =>
implicit def as: ActorSystem
implicit def mat: Materializer
implicit def ec: ExecutionContext
def request(httpRequest: HttpRequest): Future[HttpResponse] = {
sendAndReceive(httpRequest).flatMap { response =>
response.status match {
case StatusCodes.OK => Future.successful(response)
case _ => throw new RuntimeException ("error")
}
}
}
}
class ApiImpl() (implicit val as: ActorSystem, val mat: Materializer, val ec: ExecutionContext) extends Api with ApiHandler {
def request: Future[HttpResponse] = {
val httpRequest = ???
request(httpRequest)
}
}
And this is how I call request method:
implicit val actorSystem = ActorSystem("system")
implicit val executionContext = actorSystem.dispatcher
implicit val materializer = ActorMaterializer()
val api = new ApiImpl()
api.request
I receive the following error in place, where trait Api request calls sendAndReceive-method (in this line: sendAndReceive(httpRequest).flatMap { response => ):
Calling TypedActor.context outside of a TypedActor implementation method!
What am I doing wrong?

This code:
trait HttpServer {
def sendAndReceive: HttpRequest => Future[HttpResponse]
}
trait ApiHandler extends HttpServer {
implicit def as: ActorSystem
override def sendAndReceive: HttpRequest => Future[HttpResponse] =
Http().singleRequest(_)
}
trait Api {
this: ApiHandler =>
implicit def as: ActorSystem
implicit def mat: Materializer
implicit def ec: ExecutionContext
def request(httpRequest: HttpRequest) = {
sendAndReceive(httpRequest).flatMap {
response =>
response.status match {
case StatusCodes.OK => Future.successful(response)
case _ => throw new RuntimeException("error")
}
}
}
}
class ApiImpl()(implicit val as: ActorSystem, val mat: Materializer, val ec: ExecutionContext) extends Api with ApiHandler {
def request2 = {
val httpRequest = Get("https://akka.io")
request(httpRequest)
}
}
def main(args: Array[String]): Unit = {
implicit val actorSystem = ActorSystem("system")
implicit val executionContext = actorSystem.dispatcher
implicit val materializer = ActorMaterializer()
val api = new ApiImpl()
api.request2.map(println(_))
}
with versions:
val akkaVersion = "2.6.7"
val akkaHttpVersion = "10.1.12"
Works properly:
HttpResponse(200 OK,List(Date: Mon, 06 Feb 2023 12:19:39 GMT, Connection: keep-alive, Last-Modified: Tue, 17 Jan 2023 13:53:07 GMT, Vary: Accept-Encoding, CF-Cache-Status: DYNAMIC, Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=GZ7HZLwXVyB%2BXRzpcYY4A%2BjpGd0h0S8EsbSRWwMm0b8UbQrnqWpmB8e%2FFEYkNsZUVUdCWboRsWP%2B0aC6YJETfHGZU%2FU%2BFyhetR6%2FA8527KxVKfuGTjpr0Chp"}],"group":"cf-nel","max_age":604800}, NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}, Server: cloudflare, CF-RAY: 7953d5d82a3c384d-MAD, alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400),HttpEntity.Chunked(text/html),HttpProtocol(HTTP/1.1))

Related

Function never reached in Akka Streams Source mapping

Given the following code:
class MigrationHandler #Inject()(database: Database, doUpdate: UpdateHandler)
(implicit #PropagateContext executor: ExecutionContext, actorSystem: ActorSystem)
extends Handler with Logging {
implicit val materializer: ActorMaterializer = ActorMaterializer()
val buttonTypeId = "someId1"
val promotionButtonTypeId = "someId2"
val typeId3 = "someId3"
val typeId4 = "someId4"
val typeIds = Seq[String](buttonTypeId, promotionButtonTypeId, typeId3, typeId4)
def apply(requestHeaders: Headers): Seq[Future[Done]] = {
for {
typeId <- typeIds
result = database.allByType(typeId)
.map(contents => contents.map(content => migrate(content, typeId))
.map(migratedContent => doUpdate(ContentId(migratedContent.id),
NewContent(migratedContent.raw, isDefaultContent = false), requestHeaders))
.runForeach(_ => ())).flatten
} yield result
}
def migrate(content: SomeContent, typeId: String): SomeContent = {
logger.info(s"$content with type $typeId")
content
}}
Future[Source[SomeContent, _]] is returned by the database.allByType(typeId)
In unit test where I am mocking database.allByType(typeId) to return Source.single(SomeContent()), I saw that I wasn't able reach the migrate function. Any idea what could be the problem here?

I was trying to insert websocket API's in a project that uses REST API's using scala.When I compile the program this error occurs

I'm trying to establish a websocket connection. I'm trying this in a already created project. And this error occurs.
Type mismatch, expected: Flow[HttpRequest, HttpResponse, Any], actual:
server.Route
The error occurs at route
val binding = Http().bindAndHandle(route, interface, port)
println(s"Server is now online at http://$interface:$port\nPress RETURN
to stop...")
StdIn.readLine()
binding.flatMap(_.unbind()).onComplete(_ => actorSystem.terminate())
println("Server is down...")
val route=path("ws-echo") {
(get) {
val requestHandler: HttpRequest ⇒ HttpResponse = {
case req#HttpRequest( GET, Uri.Path( "/greeter" ), _, _, _ ) ⇒
req.header[UpgradeToWebSocket] match {
case Some( upgrade ) ⇒ upgrade.handleMessages(
greeterWebSocketService
)
case None ⇒ HttpResponse( 400, entity = "Not a valid websocket
request!" )
}
case r: HttpRequest =>
r.discardEntityBytes()
HttpResponse( 404, entity = "Unknown resource!" )
}
}
}
override implicit val system = ActorSystem()
override implicit val executor = system.dispatcher
override implicit val materializer = ActorMaterializer()
override val logger = Logging(system, getClass)
implicit val mat: Materializer
implicit val actorSystem = ActorSystem("akka-system")
val router: server.Route = routes
val config = ConfigFactory.load()
val interface = config.getString("app.interface")
val port = config.getInt("app.port")
Http().bindAndHandle(router, Configuration.httpInterface,
Configuration.httpPort)
I suspect you're getting this error on the line val binding = Http().bindAndHandle(route, interface, port), is that correct?
Indeed bindAndHandle expects a Flow:
def bindAndHandle(
handler: Flow[HttpRequest, HttpResponse, Any],
interface: String,
port: Int = DefaultPortForProtocol,
connectionContext: ConnectionContext = defaultServerHttpContext,
settings: ServerSettings = ServerSettings(system),
log: LoggingAdapter = system.log)(implicit fm: Materializer
): Future[ServerBinding]
So why do so many examples show passing in a Route? The 'magic' here that there's an implicit conversion available to turn a Route into a Flow.
This is basically an implementation detail, you normally shouldn't have to care about this, but the implicit conversion is route2HandlerFlow:
implicit def route2HandlerFlow(
route: Route
)(
implicit routingSettings: RoutingSettings,
parserSettings: ParserSettings,
materializer: Materializer,
routingLog: RoutingLog,
executionContext: ExecutionContext = null,
rejectionHandler: RejectionHandler = RejectionHandler.default,
exceptionHandler: ExceptionHandler = null
): Flow[HttpRequest, HttpResponse, NotUsed]
As you can see, this implicit conversion requires some other implicit values to be available. I suspect you are missing one of those. I think it might be enough to introduce an implicit Materializer, could you give that a try?

De/Serializing LocalDateTime with akka-http

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)

Do i need to create ActorMaterializer multiple times?

I have an app that generate reports, with akka-http + akka-actors + akka-camel + akka-streams. When a post request arrives , the ActiveMqProducerActor enqueue the request into ActiveMq Broker. Then the ActiveMqConsumerActor consumes the message and start the task using akka-streams(in this actor i need the materializer) .
The main class create the ActorSystem and the ActorMaterializer, but i dont know how is the correct way to "inject" the materializer into the akka-actor
object ReportGeneratorApplication extends App {
implicit val system: ActorSystem = ActorSystem()
implicit val executor = system.dispatcher
implicit val materializer = ActorMaterializer()
val camelExtension: Camel = CamelExtension(system);
val amqc: ActiveMQComponent = ActiveMQComponent.activeMQComponent(env.getString("jms.url"))
amqc.setUsePooledConnection(true)
amqc.setAsyncConsumer(true)
amqc.setTrustAllPackages(true)
amqc.setConcurrentConsumers(1)
camelExtension.context.addComponent("jms", amqc);
val jmsProducer: ActorRef = system.actorOf(Props[ActiveMQProducerActor])
//Is this the correct way to pass the materializer?
val jmsConsumer: ActorRef = system.actorOf(Props(new ActiveMQConsumerActor()(materializer)), name = "jmsConsumer")
val endpoint: ReportEndpoint = new ReportEndpoint(jmsProducer);
Http().bindAndHandle(endpoint.routes, "localhost", 8881)
}
The ReportEndPoint class, that have the jmsProducerActor . Mongo is a trait with CRUD methods. JsonSupport(==SprayJsonSupport)
class ReportEndpoint(jmsProducer: ActorRef)
(implicit val system:ActorSystem,
implicit val executor: ExecutionContext,
implicit val materializer : ActorMaterializer)
extends JsonSupport with Mongo {
val routes =
pathPrefix("reports"){
post {
path("generate"){
entity(as[DataRequest]) { request =>
val id = java.util.UUID.randomUUID.toString
// **Enqueue the request into ActiveMq**
jmsProducer ! request
val future: Future[Seq[Completed]] = insertReport(request)
complete {
future.map[ToResponseMarshallable](r => r.head match {
case r : Completed => println(r); s"Reporte Generado con id $id"
case _ => HttpResponse(StatusCodes.InternalServerError, entity = "Error al generar reporte")
})
}
}
}
} ....
The idea of ActiveMqConsumerActor, is send the messages, with streams and backpressure, one by one,because ReportBuilderActor makes many mongo operations (and the datacenter it`s not very good).
//Is this the correct way to pass the materializer??
class ActiveMQConsumerActor (implicit materializer : ActorMaterializer) extends Consumer with Base {
override def endpointUri: String = env.getString("jms.queue")
val log = Logging(context.system, this)
val reportActor: ActorRef = context.actorOf(Props(new ReportBuilderActor()(materializer)), name = "reportActor")
override def receive: Receive = {
case msg: CamelMessage => msg.body match {
case data: DataRequest => {
//I need only one task running
Source.single(data).buffer(1, OverflowStrategy.backpressure).to(Sink.foreach(d => reportActor ! d)).run()
}
case _ => log.info("Invalid")
}
case _ => UnhandledMessage
}
}
Is a good idea have implicit values in companion objects?
Thanks!!

Finding right type class to use

I'm trying to implement a type class solution for error handling in a Play application. What I want is to have some type class instances representing some validated (caught) errors and a default type class instance for any unvalidated (uncaught) errors.
I don't know if this is possible, but here's what I have so far:
trait ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit
def materialize(e: E): Result
}
trait ValidatedError[E <: Throwable] extends ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit =
ResponseError.logError(e)
}
trait UnvalidatedError[E <: Throwable] extends ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit = {
ResponseError.logError(e)
UnvalidatedError.notify(e)
}
}
object ResponseError {
def logError(e: Throwable)(implicit logger: Logger): Unit =
logger.error(e.getMessage)
}
object ValidatedError {
import java.util.concurrent.{ExecutionException, TimeoutException}
implicit val executionError = new ValidatedError[ExecutionException] {
def materialize(e: E): Result =
play.api.mvc.Results.BadRequest
}
implicit val timeoutError = new ValidatedError[TimeoutException] {
def materialize(e: E): Result =
play.api.mvc.Results.RequestTimeout
}
}
object UnvalidatedError {
implicit uncaughtError = new UnvalidatedError[Throwable] {
def materialize(e: E): Result =
play.api.mvc.Results.ServiceUnavailable
}
private def notify(e: Throwable) = ??? // send email notification
}
However how can I make sure to try my ValidatedError type class instances first, before falling back to my UnvalidatedError type class instance?
There you go. See my comment for details.
import java.util.concurrent.{TimeoutException, ExecutionException}
type Result = String
val badRequest: Result = "BadRequest"
val requestTimeout: Result = "RequestTimeout"
val serviceUnavailable: Result = "ServiceUnavailable"
class Logger {
def error(s: String) = println(s + "\n")
}
trait ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit
def materialize(e: E): Result
}
trait ValidatedError[E <: Throwable] extends UnvalidatedError[E] {
override def report(e: E)(implicit logger: Logger): Unit =
ResponseError.logError(e, validated = true)
}
trait UnvalidatedError[E <: Throwable] extends ResponseError[E] {
def report(e: E)(implicit logger: Logger): Unit = {
ResponseError.logError(e, validated = false)
UnvalidatedError.notify(e)
}
}
object ResponseError {
def logError(e: Throwable, validated: Boolean)(implicit logger: Logger): Unit =
logger.error({
validated match {
case true => "VALIDATED : "
case false => "UNVALIDATED : "
}
} + e.getMessage)
}
object ValidatedError {
import java.util.concurrent.{ExecutionException, TimeoutException}
implicit def executionError[E <: ExecutionException] = new ValidatedError[E] {
def materialize(e: E): Result =
badRequest
}
implicit def timeoutError[E <: TimeoutException] = new ValidatedError[E] {
def materialize(e: E): Result =
requestTimeout
}
}
object UnvalidatedError {
implicit def uncaughtError[E <: Throwable] = new UnvalidatedError[E] {
def materialize(e: E): Result =
serviceUnavailable
}
private def notify(e: Throwable) = println("Sending email: " + e) // send email notification
}
def testTypeclass[E](e: E)(implicit logger: Logger, ev: ResponseError[E]): Unit ={
ev.report(e)
}
import ValidatedError._
import UnvalidatedError._
implicit val logger: Logger = new Logger
val executionErr = new ExecutionException(new Throwable("execution exception!"))
testTypeclass(executionErr)
val timeoutErr = new TimeoutException("timeout exception!")
testTypeclass(timeoutErr)
val otherErr = new Exception("other exception!")
testTypeclass(otherErr)
Output:
VALIDATED : java.lang.Throwable: execution exception!
VALIDATED : timeout exception!
UNVALIDATED : other exception!
Sending email: java.lang.Exception: other exception!