Fast, testable Scala services:
If you just want to test json mapping, then yeah you can follow what happens in the Jackson tests:
Or if you want to test how object mapping interacts with HTTP requests, then you can build a minimal injector like in the MessageBodyManagerTest () and get references to the
DefaultMessageBodyReader and
DefaultMessageBodyWriter (which are supersets of logic and end up deferring to the ScalaObjectMapper for JSON serialization):
But I’m not sure that tells you anything about your code built on top of this.
Oy Folks,
Happy user of Finatra here for some time now. Just asking for an advice. We use finatra for some REST services let's say
/api/A and
/api/B with some custom request.
And that works great, but all theses services has some common parameters let's say userId so it looks like that :
trait UserTrait{ @QueryParam("user.id") val userId:String} case class GetAReQuest(@QueryParam a:String, @QueryParam("user.id") userId:String) extends UserTrait get("/api/A") { aRequest: GetARequest => /* Magic here*/ } case class GetBReQuest(@QueryParam b:Int, @QueryParam("user.id") userId:String) extends UserTrait get("/api/B") { bRequest: GetBRequest => /* Magic here*/ }
It works but I'm not fan of doing copy paste of the trait all the time (and I don't know how to make the @QueryParam used automatically if I extends the case class ) and would like to have a better mechanism to "compose" the requests object.
TL;DR I tried Request Context but at the end if I understand I need to "parse manually" the requests to my object. But I'm wondering if it would be possible to use
objectMapper.parseMessageBody[UserCaseClass](request)
without the exception
Injecting request attributes (e.g. QueryParam, Header, etc) not supported when explicitly calling ScalaObjectMapper.parse. Instead use a 'case class' input parameter on a Controller callback (e.g. get('/') { r: ClassWithRequestAttributes => ... } ).
so I just declare the case class and parsing is done automatically ?
Thanks by advance !
@totetmatt defining the
@QueryParam should only be necessary on the field in the trait. You should not need to redefine the field nor the annotation in the case class implementations like in your example. E.g. your classes should just be defined like so:
trait UserTrait{ @QueryParam("user.id") val userId:String} case class GetAReQuest(@QueryParam a:String) extends UserTrait case class GetBReQuest(@QueryParam b:Int) extends UserTrait
However, if you wanted to do this manually, you would use the
DefaultMessageBodyReader#parse method here (not the
ScalaObjectMapper#parseMessageBody):
parseMessageBodyfunction — that doesn’t know how to interpet the annotations. Only the MessageBodyReader in the HTTP package has that ability.
Hey @cacoco thanks for the insights !
I actually tried what you explain before but if I do so, scala complain as
class GetAReQuest needs to be abstract, since value userId in trait Usertrait of type String is not defined
And then, even if I add
userId:String to the case class, the
@QueryParam won't be propagated :
"user_id: field is required"
But, if on the trait I declare the attributes as
def then it works, Needs to repeat the field, but then @QueryParam looks "propagated".
trait UserTrait { @QueryParam("user.id") def userId:String} // ^---- use of def instead of val case class GetAReQuest(@QueryParam a:String, userId:String) extends UserTrait case class GetBReQuest(@QueryParam b:Int, userId:String) extends UserTrait // ^----- Repeat the trait attributes but no need to repeat @QueryParam or any other @Annotation
I don't really know what are the implication or even if it's correct to do this way, but so far it's acceptable for my use case.
case class UserData([...]) case class QueryData([...]) case class XYZData([...]) get("/api/") { req:Requests => val userData:UserData = req.asInstanceOf[UserData] val queryData:QueryData = req.asInstanceOf[QueryData] val xyzData:XYZData = req.asInstanceOf[XYZData] ^----- Or any other magical method ? // Yeah I know feels weird but just want to know for knowledge }
defin the trait and in the case class repeat it as a field (but you don’t need to redefine the annotation). This just Scala. For your second question, I’m not sure I understand what you mean by “ad-hoc” here. You have a type so it’s not really ad-hoc? If you manually want to parse the JSON body of a request, then you would just use an http Request as the callback type and then parse the content string into some object like here.
inject-mdcand
MDCInitializerfrom finatra in a finagle(finch) service, but for some reason the mdc keys aren't showing up in logs. anyone have an idea? the filter is pretty much the exact same as the LoggingMDCFilter and it's the very first filter.
finatrain the-benchmarker/web-frameworks#3835