Nested RESTful routes in CherryPy - rest

I understand and have gotten RESTful routes working in my application using this guide http://docs.cherrypy.org/dev/progguide/REST.html
Does anyone know how to add a second RESTful resource nested within a first?
I'm expecting my code to look something like this, but I can't get it to work
import cherrypy
class Pets:
exposed = True
def GET(self, personID, petID):
pass # GET /people/123/pets/333 return pet
def POST(self, personID):
pass # POST /people/123/pets create pet
class People:
pets = Pets()
exposed = True
def GET(self, personID):
pass # GET /people/123 return person
def POST(self):
pass # POST /people create person
config = {
'/people': {
'request.dispatch': cherrypy.dispatch.MethodDispatcher()
}
}
cherrypy.tree.mount(.., '/', config)

See the help docs for cherrypy.popargs. It pops path components, and supplies them as keyword arguments to the next handler. In this case, use it as a decorator on the people resource, and attach a pets resource to the people resource.
#cherrypy.popargs('petID')
class Pets:
...
#cherrypy.popargs('personID')
class People:
...

Related

Trouble using case class for multiple fields in POST body Finatra

I am working on a simple Finatra API example, but having trouble using a case class for the POST request when using more than one field in the request body.
Here is an example of my case class and controller:
class SaleResource extends Controller {
post("/sales") { sale: Sale =>
s"$sale"
}
}
case class Sale(
user: Option[String],
streetAddress: Option[String]
)
I send a post request to that route with the following request body JSON:
{
"user": "Foo Barrington",
"streetAddress":"Baz Street"
}
The response:
Sale(Some(Foo Barrington),None)
The response shows that the user is being properly deserialized, but for some reason I cannot get the streetAddress field to populate.
Also, I noticed when I set either of these fields to String instead of Option[String] I only get unsuccessful 500 responses.
Things I have tried:
case statements matching Some(streetAddress) to that fields string value or "none found" when it is None. In these cases it still is saying streetAddress is None when it is not.
Making the request with both curl and Postman.
I can always access the user field from the Sales object, but never the streetAddress (or any other field from the request body for that matter if I add test elements to the case class.
I would expect both fields to be recognized since they are both provided in the request. I am newer to Scala/Finatra in general, so it is possible I am just using the Finatra library or Case classes incorrectly.
EDIT:
It seems as if changing the field names to not be mixed/camelcase fixes all issues, but this seems like odd behavior.
Finatra uses Jackson library behind the scenes. The default configuration uses PropertyNamingStrategy.SNAKE_CASE which seems like:
{
"user": "Foo Barrington",
"street_address":"Baz Street"
}
You need to change it to PropertyNamingStrategy.LOWER_CAMEL_CASE to parse that JSON.
In order to do that, you need to define a custom FinatraJacksonModule and tell the app to
use it.
object CustomFinatraJacksonModule extends FinatraJacksonModule {
override val propertyNamingStrategy = PropertyNamingStrategy.LOWER_CAMEL_CASE
}
class MyFinatraHttpServer extends HttpServer {
override protected def jacksonModule: Module = CustomFinatraJacksonModule
}
Jackson Integration provides more information about the topic.

How to get list of all Route URL strings in play framework?

I have many controllers in my play 2.4.x application.
I want to get a list of all route URLs pointing their respective controllers. I know how to get the URL from the current request. But I need a list of all the URLs available inside a play application.I want to generate this list dynamically because URLs can be changed/added/deleted in future.
So,is there some way I can generate this URL list dynamically ? Or do I have the obligation to store all the URLs statically somewhere in cache or dictionary ?
I obtained the desired list by using the documentation method provided by Router trait. The documentation method returns Seq[(String, String, String)] .Here each tuple has the format:
( {http-method} , {url} , {controller method} )
The Router trait is extended by all the autogenerated Routes.scala classes. Scala-compiler generated a separate Routes.scala for each routes file in the application. These auto-generated Routes.scala files implement all the methods of Router trait including the documentation method that we discussed above.
So,to get list of all URLs, I simply had to inject the Router trait and then access the documentation method:
import play.api.routing.Router
class MyClass #Inject()(router: Router) {
def getAllURLs:Seq[String] = router.documentation.map(k => k._2)
}
Update for Play 2.7, Scala:
class MyController #Inject()(routesProvider: Provider[play.api.routing.Router]) {
lazy val routes: Seq[(String, String, String)] = routesProvider.get.documentation
}
from discussion for play 2.6
In response to the accepted answer: Note that if you're working in a Controller and you try to inject the Router, you'll get a Circular Dependency error (because the Router depends on your Controller). You can fix this by doing the following:
private final Provider<Router> routerProvider;
#Inject
public MainController(Provider<Router> routerProvider) {
this.routerProvider = routerProvider;
}
And then
// later in execution
Router router = routerProvider.get();
if (!router.documentation().isEmpty()) {
html.append("<ul>");
router.documentation().forEach(doc ->
html.append("<li>")
.append("<b>Method</b>: ")
.append(doc.getHttpMethod())
.append(" <b>Path</b>: ")
.append(doc.getPathPattern())
.append(" <b>Controller</b>: ")
.append(doc.getControllerMethodInvocation())
.append("</li>"));
html.append("</ul>");
}

Grails 3 Restful Link Generator (w/o the action)

Is there a way to reconfigure the Grails 3 Link Generator to create Restful links, i.e. localhost:8080/book/{id} rather than the old style that includes the action in the URL, localhost:8080/book/show/{id}?
I'd like to have restful URLs in the location headers of the responses to save actions.
I've been using this Grails Restful Link Generator as a workaround. I'm not perfectly happy with it, but it's the best I've been able to come up with thus far.
1. Create a trait in src/main/groovy that removes the superfluous action from the URL
import grails.web.mapping.LinkGenerator
trait RestfulLinkGeneratorTrait {
LinkGenerator grailsLinkGenerator
String generateLink(Map map) {
map.controller = map.controller ?: this.controllerName
map.absolute = map.absolute ?: true
map.action = map.action ?: "show"
grailsLinkGenerator.link(map).replace("/$map.action", "")
}
}
2. Implement the RestfulLinkGenerator on your controller(s) and call generateLink(id: obj.id) to generate links.
#Secured('ROLE_USER')
class BookController extends RestfulController implements RestfulLinkGeneratorTrait {
//... other methods ...//
#Transactional
def save() {
// ... save you resource ... //
response.addHeader(HttpHeaders.LOCATION, generateLink(id: book.id))
respond book, [status: CREATED, view: 'show']
}
//... other methods ...//
}

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())
}
}

Collection (as opposed to member) endpoint in Grails REST?

In rails there's an easy way to add a collection end point to routes. e.g.
resources :books do
member do
get 'publisher' # /books/id/publisher
end
collection do
get 'total_count' # /books/total_count
end
end
Is there a similar way to map the total_count endpoint in Grails? The example here ( http://grails.org/doc/2.3.1/guide/single.html#urlMappings ) only shows a member route.
"/books"(resources: "book") {
"/publisher"(controller:"publisher")
"/total_count"(controller: "publisher") // ??? can this work?
}
I am currently using Grails 2.3.4.
It was simpler than I thought though if there's a more canonical way of solving this I'd appreciate the feedback.
Basically I defined the collection endpoint before the resource endpoint.
class UrlMappings {
static mappings = {
"/books/total_count" (controller: "Book", action: "totalCount", method: "GET")
"/books" (resources: "Book")
}
}
So far it appears to be working.