Playframework Scala Async controller for JSON request - scala

I am trying to write an async PlayFramework controller that receives a POST request and creates a new object in the database:
def register = Action(BodyParsers.parse.json) { request =>
val businessInfoResult = request.body.validate[BusinessInfo]
businessInfoResult.fold(errors =>{
BadRequest(Json.obj("status"-> "Error", "message"->JsError.toJson(errors))) //Error on this line
}, businessInfo=> {
//save the object
Ok(Json.obj("status" ->"OK", "message" -> ("Place '"+ businessInfo.businessName +"' saved.") )) //Error on this line
})
}
However, it keeps throwing the error below:
reference to Json is ambiguous; it is imported twice in the same scope by import play.libs.Json and import play.mvc.BodyParser.Json AsyncController.scala
The errors are thrown at line 108 and 105 which correspond to lines commented with //Error on this line above (lines with BadRequest(..) and Ok(..))
How do I fix this issue? I can using new JsValue(Map(..)) but was wondering if there's any other way.
Thank you so much for your help.

Rather than Json, you probably want to call play.libs.Json. The problem here is that, considering the imports in your file, you have two objects / classes called Json and the compiler can't choose which one it should use. Calling play.libs.Json, you'll give the compiler enough information.

You could use one or more aliases in your imports:
import play.libs.Json
import play.mvc.BodyParser.{Json => JsonParser}
JsonParser is just an example. You can use anything you like as long as it's unique within the file.
Instead of writing Json (for play.mvc.BodyParser.Json) you could now use the alias JsonParser.
But are you sure you even need to import play.mvc.BodyParser.Json? Because you don't seem to use it.

Related

Open Web Components Testing / Lit - component not being rendered?

I'm trying to test my Lit components with #open-wc/testing. Lit has an example repo here, with this test:
https://github.com/lit/lit-element-starter-ts/blob/main/src/test/my-element_test.ts#L44
When I try to render my element like they do in their example, I get this error:
jtests/components/coding-editor.test.ts:
🚧 Browser logs:
HTMLElement: <coding-editor></coding-editor>
❌ renders
TypeError: Cannot read properties of null (reading 'querySelector')
at o.<anonymous> (jtests/components/coding-editor.test.ts:16:30)
My component works in the browser and uses the name "coding-editor". It's as if this test renderer has no idea that I'm using a custom component though. I don't know why shadowRoot is null in my case.
My code is roughly this:
import { CodingEditor } from '../../app/javascript/components/coding-editor';
import {expect, fixture} from '#open-wc/testing';
import {html} from 'lit/static-html.js';
it('renders', async () => {
const el = await fixture(html`
<coding-editor></coding-editor>
`) as CodingEditor;
console.log(el);
const text = el.shadowRoot!.querySelector('.table-constrainer');
// expect(text).to.not.be.null
});
How can I get my test to render this properly, with the shadowRoot populated?
This is likely due to TypeScript removing the CodingEditor import that's only used as a type so the side effect of defining the custom element is not happening.
You can either set the TS compiler option importsNotUsedAsValues to preserve (See https://www.typescriptlang.org/tsconfig/#importsNotUsedAsValues) or add another import line for the side-effect like
import '../../app/javascript/components/coding-editor';
Additional explanation here too: https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-imports-being-elided-in-my-emit
As a side-note, in the starter example you linked to, the imported class is used in assert.instanceOf as a value so it does not get elided by TypeScript.

Play Framework request attributes with typed key

I seem to have issues accessing the attributes of the request attributes map in Play. Following the explanation offered by Play (Link), I should get the correct data from the attributes, but the Option is returned as None.
My structure is as follows. One controller (later injected named as "sec") has the typed attribute for shared access to it:
val AuthenticatedAsAttr: TypedKey[AuthenticatedEmail] = TypedKey("AuthenticatedAs")
The type AuthenticatedEmail is defined in the companion object of this controller as a case class:
case class AuthenticatedEmail(email: String)
The filter passes the attribute to the next request:
val attrs = requestHeader.attrs + TypedEntry[AuthenticatedEmail](sec.AuthenticatedAsAttr, AuthenticatedEmail(email))
nextFilter(requestHeader.withAttrs(attrs))
When trying to then access this attribute in another controller, the returned Option is None:
val auth = request.attrs.get(sec.AuthenticatedAsAttr)
I confirmed via println that the value is definitely in request.attrs but run out of options to debug the issue successfully. A fraction of the println output below.
(Request attrs,{HandlerDef -> HandlerDef(sun.misc .... ,POST, ... Cookies -> Container<Cookies(Cookie ... , AuthenticatedAs -> AuthenticatedEmail(a#test.de), ... })
My Scala version is 2.12.6, Play Framework version 2.6.18. Any help is highly appreciated.
It turns out that the TypedKey must be within an object, not an inject-able controller. So moving it to an object like the following resolves the issue:
object Attrs {
val AuthenticatedAsAttr: TypedKey[AuthenticatedEmail] = TypedKey("AuthenticatedAs")
}
The reason is the implementation of TypedKey (Link), which does not contain an equals method and therefore reverts to comparing memory references.

Play 2.3 - Changing to WebSocket.tryAccept from async

I'm new rather new to Scala so I think this might be a very small problem.
I'm currently trying to change the method chat from using the deprecated WebSocket.async to WebSocket.tryAccept. The application uses the sample chat found at PlayFramework websocket-chat
I'm having trouble creating the complex Future type that the method requires.
This is the old method:
def chat() = WebSocket.async[JsValue] {
request =>
ChatRoom.join("User: 1")
}
New method:
def chat2() = WebSocket.tryAccept[JsValue] {
request =>
try {
// ChatRoom.join returns (iteratee,enumerator)
ChatRoom.join("User: 1").map(e => Right(e))
} catch {
case e: Exception =>
Left(Ok("Failed")) // Error here
}
}
My error message:
found : Left[Result,Nothing]
required: Future[Either[Result,(Iteratee[JsValue, _], Enumerator[JsValue])]]
I have no idea how I am supposed to create such a complex result for such a simple message.
Although ChatRoom.join("User: 1").map(e => Right(e)) doesn't show any errors now, I'm unsure if this is the correct implementation.
I'm not in front of an IDE at the moment, so I can't answer fully, but the return type it's asking for isn't as complex as it seems. An "Either" is a "Left" or a "Right" in the same way that an "Option" is a "Some" or a "None". So what it's asking for is a Future (which Websocket.async should also have required) that contains either a Left[Result] -- the fail-to-connect case, or a Right[(Iteratee, Enumerator)] -- the success case. Assuming that Chatroom.join returns a Future[(Iteratee, Enumerator)], the map operation is simply wrapping that in a "Right". The first thing I'd try is wrapping Left(Ok("Failed")) in a Future and see what happens.

AttributeError: 'Bottle' object has no attribute 'template'

Example One
Consider the following:
import bottle
import pymongo
application = bottle.Bottle()
#application.route('/')
def index():
cursor = [ mongodb query here ]
return application.template('page1',{'dbresult':cursor['content']})
Assume that the MongoDB query is correct, and the application is calling the content value of cursor correctly and passing it to the template which is formatted correctly.
The errors I am getting in the logs are to do with being able to use the template() method eg:
AttributeError: 'Bottle' object has no attribute 'template'
Example Two
If I change the corresponding assignment and call to:
application = bottle
application.template
The error is:
TypeError: 'module' object is not callable
Example Three
If I change the corresponding assignment and call to:
application = bottle
#application.route('/')
#application.view('page1.tpl')
return {'dbresult':cursor['content']}
The error is:
TypeError: 'module' object is not callable
Question
What is the correct call to the template() method to use to get Example One working?
To get "Example One" working:
return bottle.template('page1',{'dbresult':cursor['content']})
template() is in the bottle module; just reference it as bottle.template(...).
bottle.template() isn't a method of the bottle.Bottle() application object. It's a function in the bottle module.

Cannot access the parameter of a Menu.param from a Lift Snippet

I'm trying to extract the parameter from a Lift Menu.param within a snippet so that I can use it to create a named Comet. However, I get a NullPointerException when I try to pass the parameter to the snippet using SnippetDisptach in my Boot.scala, as suggested here:
http://comments.gmane.org/gmane.comp.web.lift/44299
I've created the Menu item as follows:
object AnItemPage {
// create a parameterized page
def menu = Menu.param[Item]("Item", "Item",
s => fetchItem(s), item => item._id.toString) / "item"
private def fetchItem(s:String) : Box[Item] = synchronized {
ItemDAO.findById(ObjectId.massageToObjectId(s))
}
}
I've added the menu to SiteMap. I've also created a Snippet which I would like to pick up the Item parameter. (I'm using fmpwizard's InsertNamedComet library here):
class AddCometItemPage(boxedItem: Box[Item]) extends InsertNamedComet with DispatchSnippet{
val item : Item = boxedItem.openOr(null)
override lazy val name= "comet_item_" + item._id.toString
override lazy val cometClass= "UserItemCometActor"
def dispatch = null
}
My next step is to crate an instance of this class as demonstrated by David Pollak here:
http://comments.gmane.org/gmane.comp.web.lift/44299
This is what I have added to my Boot.scala:
LiftRules.snippetDispatch.append {
case "item_page" => new AddCometItemPage(AnItemPage.menu.currentValue)
}
My item.html references this snippet:
<div class="lift:item_page">
I get the following null pointer exception when I compile and run this:
Exception occurred while processing /item/5114eb4044ae953cf863b786
Message: java.lang.NullPointerException
net.liftweb.sitemap.Loc$class.siteMap(Loc.scala:147)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.siteMap(Menu.scala:170)
net.liftweb.sitemap.Loc$class.allParams(Loc.scala:123)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.allParams(Menu.scala:170)
net.liftweb.sitemap.Loc$class.net$liftweb$sitemap$Loc$$staticValue(Loc.scala:87)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.net$liftweb$sitemap$Loc$$staticValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.paramValue(Loc.scala:85)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.paramValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.currentValue(Loc.scala:114)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.currentValue(Menu.scala:170)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:107)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:106)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.common.Full.map(Box.scala:553)
net.liftweb.util.NamedPF$.applyBox(NamedPartialFunction.scala:97)
net.liftweb.http.LiftRules.snippet(LiftRules.scala:711)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.http.LiftSession.net$liftweb$http$LiftSession$$findSnippetInstance(LiftSession.scala:1505)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1670)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1669)
Has anybody any idea where I'm going wrong? I've not been able to find a lot of information on Menu.param.
Thank you very much for your help.
f
I have never tried what you are doing, so I am not sure the best way to accomplish it. The way you are using the Loc Param, you are extracting a variable from a URL pattern. In your case, http://server/item/ITEMID where ITEMID is the string representation of an Item, and which is the value that gets passed to the fetchItem function. The function call will not have a value if you just arbitrarily call it, and from what I can see you are requesting a value that is not initialized.
I would think there are two possible solutions. The first would be to use S.location instead of AnItemPage.menu.currentValue. It will return a Box[Loc[Any]] representing the Loc that is currently being accessed (with the parameters set). You can use that Loc to retrive currentValue and set your parameter.
The other option would be to instantiate the actor in your snippet. Something like this:
item.html
<div data-lift="AnItemPage">
<div id="mycomet"></div>
</div>
And then in your AnItemPage snippet, something like this:
class AnItemPage(item: Item) {
def render = "#mycomet" #> new AddCometItemPage(item).render
}
I haven't tested either of those, so they'll probably need some tweaking. Hopefully it will give you a general idea.