I have 3 routes
POST /api/v1/items/ controllers.Application.update
POST /api/v1/items/:item_type controllers.Application.update(item_type: String)
POST /api/v1/items/:item_type/:id/ controllers.Application.update(item_type: String, id: Int)
and 3 corresponding actions for them. And one error:
[error] /my_app/conf/routes:3: method update is defined twice
[error] conflicting symbols both originated in file '/home/alex/my_app/target/scala-2.10/src_managed/main/routes_reverseRouting.scala'
[error] POST /api/v1/items/:item_type/:id/ controllers.Application.update(item_type: String, id: Int)
Please notice that should not be any default value for the parameters that is why I need these actions to be separated.
In play methods are called by name. Parameters are omitted. Name of method has to be unique. You can not have the same name for controllers (if you have in two packages)
Please use default parameters:
POST /api/v1/items/ controllers.Application.update(item_type: String = "", id: Int = 0)
POST /api/v1/items/:item_type controllers.Application.update(item_type: String, id Int =0)
POST /api/v1/items/:item_type/:id/ controllers.Application.update(item_type: String, id: Int)
Related
I've created two rest end-points in akka http which takes string as input, parse it using Json4s and then do processing on it. My case class is like -
final case class A(id: String, name: String, address: String)
1st end point receives only id while the other receives all three fields and I want to use the same case class A for both. So I used default values for name & address fields like -
final case class A(id: Stirng, name: String = "", address: String = "")
This is working good for me. But now if I don't send address or name (or both) fields at second end point, it does not throw an exception stating that the name (or address) not found.
So, my question is can I create one end point in which id is mandatory while other fields does not matter and another end point where every field is mandatory using same case class ?
The code to parse the string to a case class is -
parse(jsonStr).extract[A]
I hope you're getting my point.
Any suggestions ?
There are two ways you can achieve what you want to do.
Option + Validations
name and address are optional so you need to handle them.
case class A(id: String, name: Option[String], address: Option[String])
val json = """{ "id":"1" }"""
// 1st endpoint
val r = parse(json).extract[A]
r.name.getOrElse("foo")
r.address.getOrElse("bar")
// 2nd endpoint
val r2 = parse(json).extract[A]
r2.name.getOrElse(/* boom! */)
Default JObject + Merge
or you can use an alternative JObject to provide default values to your input.
case class A(id: String, name: String, address: String)
val json = """{ "id":"1" }"""
val defaultValues = JObject(("name", JString("foo")), ("address", JString("bar")))
// 1st endpoint
val r = defaultValues.merge(parse(json)).extract[A]
// 2nd endpoint
val r2 = parse(json).extract[A] // boom! again
No your case class formally defines what you expect in input. It doesn't represent ambiguity. You could use optional and add checks. But that just defeats the purpose of extractor.
implicit val formats = DefaultFormats
val p = each_rsvp.extract[RSVPData]
p.venue.getOrElse(default = None)
I have done the extraction using case classes. RSVPData is a case class defined as follows:
case class RSVPData(
venue: Option[VenueDetails] = None,
visibility: String,
response: String,
guests: Int,
member: MemberDetails,
rsvp_id: Long,
mtime: Long,
event: EventDetails,
group: GroupDetails
)
As shown above, parameter 'venue' is an optional field of type VenueDetails which is also a case class.
case class VenueDetails(venue_name: String, lon: Double, lat: Double, venue_id: Int)
When I do:
p.venue.getOrElse(Default=None)
I get the output as :
VenueDetails(Waller Creek Pub House,-97.73584,30.266438,24179503)
The above is returned as 'Product with Serializable'. How to extract data from VenueDetails case class separately as venue name, latitude, longitude, venue id, from this output .
I'm going to assume that p is an RSVPData item. Correct? (
When you use .getOrElse on an Option, you are attempting to provide a default value in the case where no value is defined (that is, when the value is None). In other words, getOrElse gives you an opportunity to provide default venue details when none are available, so that you can handle both cases in a consistent manner. In your case, this argument should be an instance of VenueDetails.
By passing None as the argument to getOrElse you're saying that you want None if the Option's value is None - which makes very little sense. Even worse, getOrElse now returns two types of value: a VenueDetails or None. So Scala determines the return type as those traits that are common to both of these types, which it has determined to be Product with Serializable. As a result, you can't treat the return value as a VenueDetails, and so you cannot access its members.
General rule of thumb: NEVER pass None to Option.getOrElse! ;-)
(As an aside, specifying the argument name in the call is unnecessary; p.venue.getOrElse(None) is simpler and equivalent to p.venue.getOrElse(default = None).)
So, now it depends upon what you're trying to do, which isn't clear from your question. I'll try to provide a few examples...
This first example is more imperative than functional. It attempts to print out details of the venue:
// Is the venue defined? (If None, then it's undefined.)
if (p.venue.isDefined) {
// Retrieve the defined details. Note that get throws an exception if p.venue is None.
// BTW, detail's type is inferred to be VenueDetails.
val details = p.venue.get
println(s"Venue: ${details.venue_name}, loc: (${details.lon}, ${details.lat}), id: ${details.venue_id}")
}
// Handle the case where the venue details are undefined.
else {
println("Venue details are unknown")
}
Here's a slightly more functional version:
val desc = p.venue.fold {
// This argument is evaluated only if p.venue is None.
"Venue details are unknown"
} {
// This argument is executed only if p.venue is defined (not None).
// Again, details is inferred to have type VenueDetails
details =>
s"Venue: ${details.venue_name}, loc: (${details.lon}, ${details.lat}), id: ${details.venue_id}"
}
println(desc)
This version uses a default VenueDetails instance for cases where the venue details are unknown:
// Default details for case where p.venue is None.
val defaultDetails = VenueDetails("Unknown", 0.0, 0.0, -1)
// Here, details will have type VenueDetails.
val details = p.venue.getOrElse(defaultDetails)
val desc = s"Venue: ${details.venue_name}, loc: (${details.lon}, ${details.lat}), id: ${details.venue_id}"
println(desc)
In this latter case, if p.venue is None, then the following will be printed:
Venue: Unknown, loc: (0.0, 0.0), id: -1
However, if p.venue is defined as per your example, then the following will be printed:
Venue: Waller Creek Pub House, loc: (-97.73584, 30.266438), id: 24179503
I hope this would work:
p.venue.map(_.venue_name).getOrElse(None)
I have the following case class with option fields:
case class BusinessUserRow(id: String, firstName: Option[String], lastName: Option[String], email: Option[String])
I am trying to create an inputType Object for Business User Object
val BusinessUserInputType =
deriveInputObjectType[BusinessUserRow](
InputObjectTypeName("input"),
InputObjectTypeDescription("A Business user")
)
and I want to pass this BusinessInputObject as an argument to a addBusinessUser mutation
val businessUserInputArg = Argument("input", BusinessUserInputType)
val Mutation = ObjectType("Mutation", fields[RepoContext, Unit](
Field("addBusinessUser", BusinessUserType,
arguments = businessUserInputArg :: Nil,
resolve = c ⇒ c.ctx.BusinessUserRepo.create(c.arg(businessUserInputArg)))))
But I get the following compilation error:
Type dao.Tables.BusinessUserRow ## sangria.marshalling.FromInput.InputObjectResult cannot be used as an input. Please consider defining an implicit instance of `FromInput` for it.
[error] val businessUserInputArg = Argument("input", BusinessUserInputType)
But All fields in BusinessRow are scalar values. I don't understand what is causing the issue.Is there something I am not seeing?
In order to deserialize the input in the BusinessUserRow case class, you need to provide an instance of FromInput[BusinessUserRow] type class. You can find more docs on it here:
http://sangria-graphql.org/learn/#frominput-type-class
So if you are, for instance, using spray-json, then you need to define JsonFormat for BusinessUserRow
thanks! just adding this line solved my problem:
implicit val businessUserFormat = Json.format[BusinessUserRow]
I'm new to scala.
What does following syntax mean?
case class User(val id: Long)(val username: String)
I've read about currying in scala, but explain please how it related to construction above (if related).
Thanks.
Just like partially-applied functions, a constructor (which is a function from its arguments to the constructed type) can be partially applied, in this case:
scala> case class User(val id: Long)(val username: String)
defined class User
scala> val userBuilder = User(123L) _
userBuilder: String => User = <function1>
Note the type of the resulting userBuilder - it's a function from String (the remaining parameter) to User
Now, like partially applied functions, you can apply this result to a String and get a User instance:
scala> val user = userBuilder("a")
user: User = User(123)
scala> user.username
res1: String = a
When is this useful?
When you want to construct many instances with common values for a subset of the arguments, e.g.:
case class Person(lastName: String)(val firstName: String)
class Family(lastName: String, firstNames: List[String]) {
def getMembers: List[Person] = {
val creator = Person(lastName) _ // will be reused for each first name!
firstNames.map(creator)
}
}
When you want to use one argument as the default value of another one:
case class Measure(min: Int)(val max: Int = min*2)
Measure(5)() // max = 10
Measure(5)(12) // default overridden, max = 12
When you want to use implicit arguments, which must reside in a separate, last argument list of the function, as described int the Scala Language Specification (Chapter 7.2):
A method or constructor can have only one implicit
parameter list, and it must be the last parameter list given.
It allows you to construct the object in steps.
val user = User(123L) _ // user now has only the ID
// later on
val completeUser = user("moreo") // user now also has the username
This is generally useful when you want to have your object follow an interface, but need to pass additional parameters, so you first initialise your object with those parameters and then later you get a function that can follow the interface.
I want to be able to have this:
POST /items controllers.Application.update()
POST /items/:itemType controllers.Application.update(itemType: String)
POST /items/:itemType/:id controllers.Application.update(itemType: String, id: Int)
but that doesn't compile due to the error of method update is defined twice. Then I changed it and it didn't compiler either:
POST /items controllers.Application.update(itemType: Option[String] = None, id: Option[Int] = None)
POST /items/:itemType controllers.Application.update(itemType: String, id: Option[Int] = None)
POST /items/:itemType/:id controllers.Application.update(itemType: String, id: Int)
the errors are:
the previouse one
and type mismatch; found: Option[String]; required: String
What do I do about that? I wouldn't like to do something like this:
POST /items controllers.Application.updateAll()
POST /items/:itemType controllers.Application.updateByType(itemType: String)
POST /items/:itemType/:id controllers.Application.updateByTypeAndId(itemType: String, id: Int)
and this doesn't look good either since I'd like to use Option instead of the empty string:
POST /items controllers.Application.update(itemType: String = "", id: Int = "")
POST /items/:itemType/:id controllers.Application.update(itemType: String, id: Int = "")
POST /items/:itemType/:id controllers.Application.update(itemType: String, id: Int)
Unfortunately it seems support for Option was removed in v2 - see here for example - so you may be stuck with either coding your own PathBindable to handle Options (as mentioned in the above link), or one of the other unsavoury choices you've noted.
If you're able to change your URL format, you have the ability to use Option.
Route: POST /items controllers.Application.update(itemType: Option[String], id: Option[Int])
URL: http://localhost:9000/items?itemType=someItem&id=123
With this format, you are able to omit itemType, id, or both when making the web service call.