How can I log a warning when I "halt()" in Scalatra? - scala

In my Scalatra routes, I often use halt() to fail fast:
val user: User = userRepository.getUserById(params("userId"))
.getOrElse {
logger.warn(s"Unknown user: $userId")
halt(404, s"Unknown user: $userId")
}
As shown in the example, I also want to log a warning in those cases. But I'd like to avoid the code duplication between the halt() and the logger. It would be a lot cleaner to simply do:
val user: User = userRepository.getUserById(params("userId"))
.getOrElse(halt(404, s"Unknown user: $userId"))
What would be the best way of logging all "HaltExceptions" in a cross-cutting manner ?
I've considered:
1) Overriding the halt() method in my route:
override def halt[T](status: Integer, body: T, headers: Map[String, String])(implicit evidence$1: Manifest[T]): Nothing = {
logger.warn(s"Halting with status $status and message: $body")
super.halt(status, body, headers)
}
Aside from the weird method signature, I don't really like this approach, because I could be calling the real halt() by mistake instead of the overridden method, for example if I'm halting outside the route. In this case, no warning would be logged.
2) Use trap() to log all error responses:
trap(400 to 600) {
logger.warn(s"Error returned with status $status and body ${extractBodyInSomeWay()}")
}
But I'm not sure it's the best approach, especially since it adds 201 routes to the _statusRoutes Map (one mapping for each integer in the range...). I also don't know how to extract the body here ?
3) Enable some kind of response logging in Jetty for specific status codes ?
What would be the best approach to do this? Am I even approaching this correctly?

The easiest solution is doing it in a servlet filter like below:
package org.scalatra.example
import javax.servlet._
import javax.servlet.http.HttpServletResponse
class LoggingFilter extends Filter {
override def init(filterConfig: FilterConfig): Unit = ()
override def destroy(): Unit = ()
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = {
chain.doFilter(request, response)
val status = response.asInstanceOf[HttpServletResponse].getStatus
if (status >= 400 && status <= 600) {
// Do logging here!
}
}
}
Register this filter in your Bootstrap class (or it's possible even in web.xml):
package org.scalatra.example
import org.scalatra._
import javax.servlet.ServletContext
class ScalatraBootstrap extends LifeCycle {
override def init(context: ServletContext): Unit = {
context.addFilter("loggingFilter", new LoggingFilter())
context.getFilterRegistration("loggingFilter")
.addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*")
// mount your servlets or filters
...
}
}
In my opinion, Scalatra should provide a way to trap halting easier essentially. In fact, there is a method named renderHaltException in ScalatraBase, it looks to be possible to add logging by overriding this method at a glance:
https://github.com/scalatra/scalatra/blob/cec3f75e3484f2233274b1af900f078eb15c35b1/core/src/main/scala/org/scalatra/ScalatraBase.scala#L512
However we can't do it actually because HaltException is package private and it can be accessed inside of org.scalatra package only. I wonder HaltException should be public.

Related

Embedding multiple tests inside an akka-http route check

I'm using akka-http for the first time - my usual web framework of choice is http4s - and I'm having trouble getting the way I usually write endpoint unit tests to work with the route testing provided by akka-http-testkit.
Generally, I use ScalaTest (FreeSpec flavour) in order to set up an endpoint call and then run several separate tests on the response. For akka-http-testkit, this would look like:
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalatest.{FreeSpec, Matchers}
final class Test extends FreeSpec with ScalatestRouteTest with Matchers {
val route: Route = path("hello") {
get {
complete("world")
}
}
"A GET request to the hello endpoint" - {
Get("/hello") ~> route ~> check {
"should return status 200" in {
status should be(StatusCodes.OK)
}
"should return a response body of 'world'" in {
responseAs[String] should be("world")
}
//more tests go here
}
}
}
This errors with
java.lang.RuntimeException: This value is only available inside of a `check` construct!
The problem is the nested tests inside the check block - for some reason, values like status and responseAs are only available top-level within that block. I can avoid the error by saving the values I'm interested in to local variables top-level, but that's awkward and capable of crashing the test framework if e.g. the response parsing fails.
Is there a way around this, without putting all my assertions into a single test or performing a new request for each one?
You can group your test like that
"A GET request to the hello endpoint should" in {
Get("/hello") ~> route ~> check {
status should be(StatusCodes.OK)
responseAs[String] should be("world")
//more tests go here
}
}

scala: Moking my scala Object that has external dependency

I have a Object like this:
// I want to test this Object
object MyObject {
protected val retryHandler: HttpRequestRetryHandler = new HttpRequestRetryHandler {
def retryRequest(exception: IOException, executionCount: Int, context: HttpContext): Boolean = {
true // implementation
}
}
private val connectionManager: PoolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager
val httpClient: CloseableHttpClient = HttpClients.custom
.setConnectionManager(connectionManager)
.setRetryHandler(retryHandler)
.build
def methodPost = {
//create new context and new Post instance
val post = new HttpPost("url")
val res = httpClient.execute(post, HttpClientContext.create)
// check response code and then take action based on response code
}
def methodPut = {
// same as methodPost except use HttpPut instead HttpPost
}
}
I want to test this object by mocking dependent objects like httpClient. How to achieve this? can i do it using Mokito or any better way? If yes. How? Is there a better design for this class?
Your problem is: you created hard-to test code. You can turn here to watch some videos to understand why that is.
The short answer: directly calling new in your production code always makes testing harder. You could be using Mockito spies (see here on how that works).
But: the better answer would be to rework your production code; for example to use dependency injection. Meaning: instead of creating the objects your class needs itself (by using new) ... your class receives those objects from somewhere.
The typical (java) approach would be something like:
public MyClass() { this ( new SomethingINeed() ); }
MyClass(SomethingINeed incoming) { this.somethign = incoming; }
In other words: the normal usage path still calls new directly; but for unit testing you provide an alternative constructor that you can use to inject the thing(s) your class under test depends on.

Specs2/Guice issue in Play 2.4.0 functional tests

I'm having an issue with dependencies apparently bleeding between tests, which is causing most of the tests to fail. In each case, debugging shows the first app created in a test class is used for all tests, and this is resulting in the failures.
I've tried adding isolated and sequential and this has had no effect.
Am I doing something remarkably stupid or subtly stupid?
For example, here's SubjectNotPresentTest.scala
class SubjectNotPresentTest extends AbstractViewTest {
"show constrained content when subject is not present" in new WithApplication(testApp(handler())) {
val html = subjectNotPresentContent(FakeRequest())
private val content: String = Helpers.contentAsString(html)
content must contain("This is before the constraint.")
content must contain("This is protected by the constraint.")
content must contain("This is after the constraint.")
}
"hide constrained content when subject is present" in new WithApplication(testApp(handler(subject = Some(user())))) {
val user = new User("foo", Scala.asJava(List.empty), Scala.asJava(List.empty))
val html = subjectNotPresentContent(FakeRequest())
private val content: String = Helpers.contentAsString(html)
content must contain("This is before the constraint.")
content must not contain("This is protected by the constraint.")
content must contain("This is after the constraint.")
}
}
GuiceApplicationBuilder is used in a parent class is used to create the app for testing.
val app = new GuiceApplicationBuilder()
.bindings(new DeadboltModule())
.bindings(bind[HandlerCache].toInstance(LightweightHandlerCache(handler)))
.overrides(bind[CacheApi].to[FakeCache])
.in(Mode.Test)
.build()
You can see an example of the failures at https://travis-ci.org/schaloner/deadbolt-2-scala/builds/66369307#L805
All tests can be found at https://github.com/schaloner/deadbolt-2-scala/tree/master/code/test/be/objectify/deadbolt/scala/views
Thanks,
Steve
It looks like the problem is caused when the current Play application is statically referenced in a test environment in which there are multiple applications - even if they are logically separate.
Because components can't be injected (to the best of my knowledge) into templates, I created a helper object which uses Play.current.injector to define a couple of vals.
val viewSupport: ViewSupport = Play.current.injector.instanceOf[ViewSupport]
val handlers: HandlerCache = Play.current.injector.instanceOf[HandlerCache]
(It's also not possible, TTBOMK, to inject into objects, otherwise I could just inject the components into the object and everyone could go home).
A better approach is to expose what is required as an implicit.
object ViewAccessPoint {
private[deadbolt] val viewStuff = Application.instanceCache[ViewSupport]
private[deadbolt] val handlerStuff = Application.instanceCache[HandlerCache]
object Implicits {
implicit def viewSupport(implicit application: Application): ViewSupport = viewStuff(application)
implicit def handlerCache(implicit application: Application): HandlerCache = handlerStuff(application)
}
}
In the view, import the implicits and you're good to go.
#import be.objectify.deadbolt.scala.DeadboltHandler
#import be.objectify.deadbolt.scala.ViewAccessPoint.Implicits._
#import play.api.Play.current
#(handler: DeadboltHandler = handlerCache(current).apply(), name: String, meta: String = null, timeout: Function0[Long] = viewSupport.defaultTimeout)(body: => play.twirl.api.Html)(implicit request: Request[Any])
#if(viewSupport.dynamic(name, meta, handler, timeout(), request)) {
#body
}

Scala design suggestion needed

I would like to design a client that would talk to a REST API. I have implemented the bit that actually does call the HTTP methods on the server. I call this Layer, the API layer. Each operation the server exposes is encapsulated as one method in this layer. This method takes as input a ClientContext which contains all the needed information to make the HTTP method call on the server.
I'm now trying to set up the interface to this layer, let's call it ClientLayer. This interface will be the one any users of my client library should use to consume the services. When calling the interface, the user should create the ClientContext, set up the request parameters depending on the operation that he is willing to invoke. With the traditional Java approach, I would have a state on my ClientLayer object which represents the ClientContext:
For example:
public class ClientLayer {
private static final ClientContext;
...
}
I would then have some constructors that would set up my ClientContext. A sample call would look like below:
ClientLayer client = ClientLayer.getDefaultClient();
client.executeMyMethod(client.getClientContext, new MyMethodParameters(...))
Coming to Scala, any suggestions on how to have the same level of simplicity with respect to the ClientContext instantiation while avoiding having it as a state on the ClientLayer?
I would use factory pattern here:
object RestClient {
class ClientContext
class MyMethodParameters
trait Client {
def operation1(params: MyMethodParameters)
}
class MyClient(val context: ClientContext) extends Client {
def operation1(params: MyMethodParameters) = {
// do something here based on the context
}
}
object ClientFactory {
val defaultContext: ClientContext = // set it up here;
def build(context: ClientContext): Client = {
// builder logic here
// object caching can be used to avoid instantiation of duplicate objects
context match {
case _ => new MyClient(context)
}
}
def getDefaultClient = build(defaultContext)
}
def main(args: Array[String]) {
val client = ClientFactory.getDefaultClient
client.operation1(new MyMethodParameters())
}
}

Adding functionality to Grails restfulcontroller

I'm having a very simple restful controller, which looks like this:
class PersonController extends RestfulController<Person> {
static responseFormats = ['json', 'xml']
PersonController() {
super(Person)
}
}
However, now I want to add a search option to this. What is the Grails way of making this possible?
I thought of adding the following:
def search(Map params) {
println params
}
But that makes Grails (2.3) crash (| Error Fatal error during compilation org.apache.tools.ant.BuildException: Compilation Failed (Use --stacktrace to see the full trace)).
So what is the right way of adding this? I'm looking for some solution which I can call using http://localhost:8080/foo/person/search?q=erik
This is my UrlMappings:
static mappings = {
"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
}
}
"/rest/persons"(resources:'Person')
I've changed the above to:
def search() {
println params
}
And that doesn't give the compilation error anymore, but I still get this error:
TypeMismatchException occurred when processing request: [GET] /declaratie-web/rest/medicaties/search - parameters:
q: erik
Provided id of the wrong type for class nl.Person. Expected: class java.lang.Long, got class java.lang.String. Stacktrace follows:
org.hibernate.TypeMismatchException: Provided id of the wrong type for class nl.Person. Expected: class java.lang.Long, got class java.lang.String
I also found out that it doesn't matter how I call the controller:
http://localhost:8080/foo/person/search?q=erik
http://localhost:8080/foo/person/search222?q=erik
http://localhost:8080/foo/person/search39839329?q=erik
All fails with the above error, so it seems my method is ignored (maybe caused by my URLmapping?)
You really aren't being RESTful by doing that. q should just be a parameter for the index action. You can override that method to include your functionality.
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
def c = Person.createCriteria()
def results = c.list(params) {
//Your criteria here with params.q
}
respond results, model:[personCount: results.totalCount]
}
#james-kleeh solution is right, but you can do it more clean by override the listAllResources method which is called by index
#Override
protected List<Payment> listAllResources(Map params) {
Person.createCriteria().list(params) {
// Your criteria here with params.q
}
}