scala code can be ran in command line but eclipse - scala

import com.twitter.finagle.{Http, Service}
import com.twitter.util.{Await, Future}
import java.net.InetSocketAddress
import org.jboss.netty.handler.codec.http._
object Client extends App {
//#builder
val client: Service[HttpRequest, HttpResponse] =
Http.newService("www.google.com:80")
//#builder
//#dispatch
val request = new DefaultHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, "/")
val response: Future[HttpResponse] = client(request)
//#dispatch
//#callback
response onSuccess { resp: HttpResponse =>
println("GET success: " + resp)
}
Await.ready(response)
//#callback
}
I have this code
when I run it from command line, it works
./sbt 'run-main Proxy'
But when I run it from scala ide(eclipse)
it gave me
Error: Could not find or load main class Server
why is that? how can I fix this?
thanks

Related

Allowing illegal requests to webserver

I am making a akka webserver, and running into an issue. When receiving a request that contains a % character, I get the following error: Illegal request, responding with status '400 Bad Request': Illegal request-target: Invalid input '%', expected HEXDIG (line 1, column 12): /path?val=%%test%%. While I understand that %% is a bad encoding, I would like to log and process the request, since I do receive them. Is there anyway to stop the auto 400 response, and receive the data as received? Here is a minimal working example of my program:
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model.{ ContentTypes, HttpEntity }
import akka.http.scaladsl.server.Directives._
import scala.io.StdIn
import akka.http.scaladsl.server._
import akka.actor.Props
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.HttpMethods._
import akka.stream.scaladsl.Sink
import akka.stream.scaladsl.Flow
import Directives._
object Main extends App {
implicit def myRejectionHandler =
RejectionHandler.newBuilder()
.handle {
case _: IllegalUriException =>
println("Illegal URI rejection")
complete(HttpResponse(200))
}
.handleAll[MethodRejection] { methodRejections =>
println("Handle all")
complete(HttpResponse(200))
}
.result()
implicit def myExceptionHandler: ExceptionHandler =
ExceptionHandler {
case _: IllegalUriException =>
println ("Illegal URI Exception")
complete(HttpResponse(200))
}
implicit val system = ActorSystem()
implicit val executionContext = system.dispatcher
val route = {
handleExceptions(myExceptionHandler) {
get {
path("path") {
complete("path!")
}
}
}
}
val bindingFuture = Http().newServerAt("0.0.0.0", 80).bind(route)
println(s"Server online at http://localhost:80/\nPress RETURN to stop...")
StdIn.readLine()
bindingFuture
.flatMap(_.unbind())
.onComplete(_ => system.terminate())
}
I also did attempt to set
akka.http {
parsing {
uri-parsing-mode = relaxed
}
}
But did not solve this issue

how to mock external WS API calls in Scala Play framework

I have an existing Scala play application which has a REST API that calls another external REST API. I want to mock the external Web service returning fake JSON data for internal tests. Based on example from: https://www.playframework.com/documentation/2.6.x/ScalaTestingWebServiceClients
I followed example exactly as in Documentation and I'm getting compiler errors due to deprecated class Action.
import play.core.server.Server
import play.api.routing.sird._
import play.api.mvc._
import play.api.libs.json._
import play.api.test._
import scala.concurrent.Await
import scala.concurrent.duration._
import org.specs2.mutable.Specification
import product.services.market.common.GitHubClient
class GitHubClientSpec extends Specification {
import scala.concurrent.ExecutionContext.Implicits.global
"GitHubClient" should {
"get all repositories" in {
Server.withRouter() {
case GET(p"/repositories") => Action {
Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
}
} { implicit port =>
WsTestClient.withClient { client =>
val result = Await.result(
new GitHubClient(client, "").repositories(), 10.seconds)
result must_== Seq("octocat/Hello-World")
}
}
}
}
}
object Action in package mvc is deprecated: Inject an ActionBuilder
(e.g. DefaultActionBuilder) or extend
BaseController/AbstractController/InjectedController
And this is the primary example from latest official docs which in fact contains a compile time error, given this example doesn't work how should be the proper way to easily mock an external API using Scala Play?
You may change your example to:
Server.withRouterFromComponents() { cs => {
case GET(p"/repositories") => cs.defaultActionBuilder {
Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
}
}
} { implicit port =>
WsTestClient.withClient { client =>
val result = Await.result(
new GitHubClient(client, "").repositories(), 10.seconds)
result should be(Seq("octocat/Hello-World"))
}
}
To be honest, I'm not 100% sure if this is the nicest way. However I have submitted a PR to the play framework so you might watch that space for comments from the makers.
If you're using standalone version of play-ws you can use this library https://github.com/f100ded/play-fake-ws-standalone like this
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import org.f100ded.play.fakews._
import org.scalatest._
import play.api.libs.ws.JsonBodyWritables._
import scala.concurrent.duration.Duration
import scala.concurrent._
import scala.language.reflectiveCalls
/**
* Tests MyApi HTTP client implementation
*/
class MyApiClientSpec extends AsyncFlatSpec with BeforeAndAfterAll with Matchers {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
import system.dispatcher
behavior of "MyApiClient"
it should "put access token to Authorization header" in {
val accessToken = "fake_access_token"
val ws = StandaloneFakeWSClient {
case request # GET(url"http://host/v1/foo/$id") =>
// this is here just to demonstrate how you can use URL extractor
id shouldBe "1"
// verify access token
request.headers should contain ("Authorization" -> Seq(s"Bearer $accessToken"))
Ok(FakeAnswers.foo)
}
val api = new MyApiClient(ws, baseUrl = "http://host/", accessToken = accessToken)
api.getFoo(1).map(_ => succeed)
}
// ... more tests
override def afterAll(): Unit = {
Await.result(system.terminate(), Duration.Inf)
}
}

java.lang.RuntimeException: There is no started application error, when testing a class from scala worksheet

I wanted to test a class by running it from a scala worksheet. When running this test script:
import ping.GcmRestServer
val server = new GcmRestServer("AIzaSyCOn...")
server.send(List("dcGKzDg5VOQ:APA91bHNUDaBj01th..."), Map(
"message" -> "Test Message",
"title" -> "Test Title"
))
Were the tested class GcmRestServer is
package ping
import play.api.Logger
import play.api.libs.ws.WS
import play.api.libs.json.Json
/**
* Created by Lukasz on 26.02.2016.
*/
class GcmRestServer(val key: String) {
def send(ids: List[String], data: Map[String, String]) = {
import play.api.Play.current
import scala.concurrent.ExecutionContext.Implicits.global
val body = Json.obj(
"registration_ids" -> ids,
"data" -> data
)
WS.url("https://android.googleapis.com/gcm/send")
.withHeaders(
"Authorization" -> s"key=$key",
"Content-type" -> "application/json"
)
.post(body)
.map { response => Logger.debug("Result: " + response.body)}
}
}
Gives the following result:
import ping.GcmRestServer
server: ping.GcmRestServer = ping.GcmRestServer#34860db2
java.lang.RuntimeException: There is no started application
at scala.sys.package$.error(test.sc2.tmp:23)
at play.api.Play$$anonfun$current$1.apply(test.sc2.tmp:82)
at play.api.Play$$anonfun$current$1.apply(test.sc2.tmp:82)
at scala.Option.getOrElse(test.sc2.tmp:117)
at play.api.Play$.current(test.sc2.tmp:82)
at ping.GcmRestServer.send(test.sc2.tmp:16)
at #worksheet#.get$$instance$$res0(test.sc2.tmp:4)
at #worksheet#.#worksheet#(test.sc2.tmp:19)
Could somebody explain me what I did wrong, and how to fix this?
The line import play.api.Play.current requires a running Play application.
I've never used scala worksheets, but it seems the same problem.
On tests, the solution is to run a fake application, as written in the documentation.
You are using play WS which requires a play application running. Just start one with a code like this:
import play.api.libs.ws.WS
import play.api.{Play, Mode, DefaultApplication}
import java.io.File
import play.api.Logger
import play.api.libs.json.Json
import scala.concurrent.ExecutionContext.Implicits.global
class GcmRestServer(val key: String) {
val application = new DefaultApplication(
new File("."),
Thread.currentThread().getContextClassLoader(),
None,
Mode.Dev
)
def send(ids: List[String], data: Map[String, String]) = {
val body = Json.obj(
"registration_ids" -> ids,
"data" -> data
)
WS.client(application).url("https://android.googleapis.com/gcm/send")
.withHeaders(
"Authorization" -> s"key=$key",
"Content-type" -> "application/json"
)
.post(body)
.map { response => Logger.debug("Result: " + response.body)}
}
}
Or if you don't want to use WS.Client(application) you may run Play.start(application), import it with import play.api.Play.current and then WS.url will work.

How I can make a request with params from finagle client?

I’m getting started with a Finagle server (twitter/finagle):
import com.twitter.finagle.{Http, Service}
import com.twitter.util.{Await, Future}
import java.net.InetSocketAddress
import org.jboss.netty.handler.codec.http._
object Server extends App {
val service = new Service[HttpRequest, HttpResponse] {
def apply(req: HttpRequest): Future[HttpResponse] =
Future.value(new DefaultHttpResponse(
req.getProtocolVersion, HttpResponseStatus.OK))
}
val server = Http.serve(":8080", service)
Await.ready(server)
}
Client (twitter/finagle):
import com.twitter.finagle.{Http, Service}
import com.twitter.util.{Await, Future}
import java.net.InetSocketAddress
import org.jboss.netty.handler.codec.http._
object Client extends App {
val client: Service[HttpRequest, HttpResponse] =
Http.newService("localhost:8080")
val request = new DefaultHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.GET, "/")
val response: Future[HttpResponse] = client(request)
response onSuccess { resp: HttpResponse =>
println("GET success: " + resp)
}
Await.ready(response)
}
How do I send data like Map("data_id" -> 5) from the client to the server? And where in the server do I receive it? Do I have to add a callback to the server?
I haven’t found it by searching. If you can give me a link with an example, that will be enough.
Finagle is a very thin library. That means that you'll have to handle most of the "magic" by yourself.
To make the request with parameters from the Client, I use these helper methods:
def buildUri(base: String, path: String, params: Map[String, String] = Map.empty): String = {
val p = if (params.isEmpty) ""
else params map { case (k,v) => urlEncode(k) + "=" + urlEncode(v) } mkString ("?", "&", "")
base + path + p
}
def urlEncode(url: String): String = URLEncoder.encode(url, "UTF-8")
And then I call it like this:
val url = buildUri(baseAddress, path, defaultParams ++ params)
val req = RequestBuilder().url(url).setHeader("Accept", "*/*").buildGet
client(req)
As for the server you have to do basically the same thing and parse the parameters by hand. Either using java.net.URI or even org.jboss.netty.handler.codec.http.QueryStringDecoder.
Of course you can also use URI and QueryStringEncoder to encode as well, instead of using my helper methods.
That said, if you want to do that on higher level, you can use one of these libraries above Finagle:
https://github.com/fwbrasil/zoot
http://finatra.info/ (this is for the server part only)

Exception when using elastic4s with elastic-search and spray-routing

I'm trying to write a little REST api using Scala, Spray.io, Elastic4s and ElasticSearch.
My ES instance is running with default parameters, I just changed the parameter network.host to 127.0.0.1.
Here is my spray routing definition
package com.example
import akka.actor.Actor
import spray.routing._
import com.example.core.control.CrudController
class ServiceActor extends Actor with Service {
def actorRefFactory = context
def receive = runRoute(routes)
}
trait Service extends HttpService {
val crudController = new CrudController()
val routes = {
path("ads" / IntNumber) {
id =>
get {
ctx =>
ctx.complete(
crudController.getFromElasticSearch
)
}
}
}
}
My crudController :
package com.example.core.control
import com.example._
import org.elasticsearch.action.search.SearchResponse
import scala.concurrent._
import scala.util.{Success, Failure}
import ExecutionContext.Implicits.global
class CrudController extends elastic4s
{
def getFromElasticSearch : String = {
val something: Future[SearchResponse] = get
something onComplete {
case Success(p) => println(p)
case Failure(t) => println("An error has occured: " + t)
}
"GET received \n"
}
}
And a trait elastic4s who is encapsulating the call to elastic4s
package com.example
import com.sksamuel.elastic4s.ElasticClient
import com.sksamuel.elastic4s.ElasticDsl._
import scala.concurrent._
import org.elasticsearch.action.search.SearchResponse
trait elastic4s {
def get: Future[SearchResponse] = {
val client = ElasticClient.remote("127.0.0.1", 9300)
client execute { search in "ads"->"categories" }
}
}
This code runs well, and gives me this output :
[INFO] [03/26/2014 11:41:50.957] [on-spray-can-akka.actor.default-dispatcher-4] [akka://on-spray-can/user/IO-HTTP/listener-0] Bound to localhost/127.0.0.1:8080
But when a try to access to the route "localhost/ads/8" with my browser, the case Failure is always triggered and I got this error output on my intellij console :
An error has occured: org.elasticsearch.transport.RemoteTransportException: [Skinhead][inet[/127.0.0.1:9300]][search]
(No console output with elasticSearch running on my terminal)
Is this exception related to ElasticSearch, or am I doing wrong with my Future declaration ?
I suppose you should use ElasticClient.local in this case, as specified in elastic4s docs:
https://github.com/sksamuel/elastic4s
To specify settings for the local node you can pass in a settings object like this:
val settings = ImmutableSettings.settingsBuilder()
.put("http.enabled", false)
.put("path.home", "/var/elastic/")
val client = ElasticClient.local(settings.build)