Leaf Resources with child Leaf Resources

From what I understand, once a Resource isLeaf = True, it cannot have child
Resources of its own (no requests seem to get routed to them).
This is not really a realistic scenario in a typical REST application where nested REST services are common, e.g.
Customer REST service:
*GET /services/customer* *POST /services/customer* *PUT /services/customer/<customerId>* *DELETE /services/customer/<customerId>*
Customer Address REST service:
*GET /services/customer/<customerId>/address* *POST /services/customer/<customerId>/address* *PUT /services/customer/<customerId>/address/<addressId>* *DELETE /services/customer/<customerId>/address/<addressId>*
and so on and so forth....
The only way I can support this in CorePost is to separate the concept of a Twisted.Web Resource from a standalone REST service for a particular entity.
So let's say I would have a root CorePost Resource hooked up to 'services' and it would have a child collection of REST service classes and manage routing the requests to the appropriate one. Each of the REST services for an entity underneath that core Resource would NOT be a twisted.web Resource but just a regular class.
Does this sound correct? Or am I missing some way of using twisted.web Resource objects that would allow me to accomplish the same thing without moving away from Resource as the ancestor of all my REST service classes?
Thanks Jacek

On 05:05 pm, jacek99@gmail.com wrote:
From what I understand, once a Resource isLeaf = True, it cannot have child
Resources of its own (no requests seem to get routed to them).
This is not really a realistic scenario in a typical REST application where nested REST services are common, e.g.
Sorry, perhaps it would be obvious if I looked at the CorePost implementation or read its documentation, but I don't understand what problem you're having or what question you're asking.
If setting `isLeaf` to `True` isn't appropriate for CorePost's purposes, then don't set it to `True`?
Jean-Paul
Customer REST service:
*GET /services/customer* *POST /services/customer* *PUT /services/customer/<customerId>* *DELETE /services/customer/<customerId>*
Customer Address REST service:
*GET /services/customer/<customerId>/address* *POST /services/customer/<customerId>/address* *PUT /services/customer/<customerId>/address/<addressId>* *DELETE /services/customer/<customerId>/address/<addressId>*
and so on and so forth....
The only way I can support this in CorePost is to separate the concept of a Twisted.Web Resource from a standalone REST service for a particular entity.
So let's say I would have a root CorePost Resource hooked up to 'services' and it would have a child collection of REST service classes and manage routing the requests to the appropriate one. Each of the REST services for an entity underneath that core Resource would NOT be a twisted.web Resource but just a regular class.
Does this sound correct? Or am I missing some way of using twisted.web Resource objects that would allow me to accomplish the same thing without moving away from Resource as the ancestor of all my REST service classes?
Thanks Jacek

Sorry, let me be more clear:
I have a Resource let's say CustomerRestService which is a leaf and handles everything related to '/customer' Then I want a CustomerAddressRestService Resource, which is also a leaf and should handle everything related to '/customer/<customerId>/address'.
So I need a leaf Resource (i.e. it actually intercepts GET/POST/PUT/DELETE requests to the root '/customer' URL) with a child leaf Resource (which intercepts the GET/POST/PUT/DELETE requests to its root '/customer/<customerId>/address' URL).
Twisted does not really allow for nested leaf resources, but REST is all about nested URL schemes, e.g.
Customer -> Customer Address Customer -> Customer Phone Customer -> Customer Invoice -> Customer Payment
etc.
Taking CorePost out of the picture and just going back to raw twisted.web, how would you recommend that be done?
Jacek

On 06:09 pm, jacek99@gmail.com wrote:
Sorry, let me be more clear:
I have a Resource let's say CustomerRestService which is a leaf and handles everything related to '/customer' Then I want a CustomerAddressRestService Resource, which is also a leaf and should handle everything related to '/customer/<customerId>/address'.
Well.... my first reaction is that this doesn't make sense. Leaves don't have leaves. That's what makes them leaves.
Your CustomerRestService is not a leaf resource.
Why do you think it should be one?
So I need a leaf Resource (i.e. it actually intercepts GET/POST/PUT/DELETE requests to the root '/customer' URL) with a child leaf Resource (which intercepts the GET/POST/PUT/DELETE requests to its root '/customer/<customerId>/address' URL).
Twisted does not really allow for nested leaf resources, but REST is all about nested URL schemes, e.g.
Right. Because "nested leaf resource" is self contradictory.
Customer -> Customer Address Customer -> Customer Phone Customer -> Customer Invoice -> Customer Payment
etc.
Taking CorePost out of the picture and just going back to raw twisted.web, how would you recommend that be done?
Apart from what I said above, and still not really understanding why you want this, the implementation of the traversal mechanism is exposed as `twisted.web.resource.getChildForRequest`. After you stop traversal with your first "leaf" resource, you can finish it by calling `getChildForRequest` manually, and then manually rendering the resulting resource.
Jean-Paul

See comments below
On Wed, Feb 29, 2012 at 2:45 PM, exarkun@twistedmatrix.com wrote:
On 06:09 pm, jacek99@gmail.com wrote:
Well.... my first reaction is that this doesn't make sense. Leaves don't have leaves. That's what makes them leaves.
Your CustomerRestService is not a leaf resource.
it handles GET/POST/PUT/DELETE requests for the Customer entity. If I am coding in raw twisted.web, that means I would need to handle the render_GET, render_POST, etc. in this class.
Isn't that what classifies it as a leaf resource (i.e. one that actually handles HTTP requests instead of just passing them down to a child)?
about nested URL schemes, e.g.
Right. Because "nested leaf resource" is self contradictory.
Well, not in REST...nested resources under another other is the norm in defining URL schemes in REST apps. Or does the concept of a REST service for an entity simply not map at all to what Twisted Web defines as a Resource?
Apart from what I said above, and still not really understanding why you
want this, the implementation of the traversal mechanism is exposed as `twisted.web.resource.getChildForRequest`. After you stop traversal with your first "leaf" resource, you can finish it by calling `getChildForRequest` manually, and then manually rendering the resulting resource.
Let me look at that. But it seems counter-intuitive for a typical REST app structure....
Jacek

On 07:53 pm, jacek99@gmail.com wrote:
See comments below
On Wed, Feb 29, 2012 at 2:45 PM, exarkun@twistedmatrix.com wrote:
On 06:09 pm, jacek99@gmail.com wrote:
Well.... my first reaction is that this doesn't make sense. Leaves don't have leaves. That's what makes them leaves.
Your CustomerRestService is not a leaf resource.
it handles GET/POST/PUT/DELETE requests for the Customer entity. If I am coding in raw twisted.web, that means I would need to handle the render_GET, render_POST, etc. in this class.
Isn't that what classifies it as a leaf resource (i.e. one that actually handles HTTP requests instead of just passing them down to a child)?
If CustomerRestService is the resource for requests like:
GET /services POST /services PUT /services DELETE /services
That does not make it a leaf resource. That just makes it a resource.
If CustomerRestService is the resource for requests like:
GET/POST/PUT/DELETE /services GET/POST/PUT/DELETE /services/something/else
That makes it a leaf resource.
about nested URL schemes, e.g.
Right. Because "nested leaf resource" is self contradictory.
Well, not in REST...nested resources under another other is the norm in defining URL schemes in REST apps.
I don't think REST has anything to say about "leaf resources", because they're an API convenience invented by Twisted Web.
Jean-Paul
Or does the concept of a REST service for an entity simply not map at all to what Twisted Web defines as a Resource?
Apart from what I said above, and still not really understanding why you
want this, the implementation of the traversal mechanism is exposed as `twisted.web.resource.getChildForRequest`. After you stop traversal with your first "leaf" resource, you can finish it by calling `getChildForRequest` manually, and then manually rendering the resulting resource.
Let me look at that. But it seems counter-intuitive for a typical REST app structure....
Jacek

OK, so the issue I have is that I cannot create two separate REST Resources with nested URLs e.g.
class CustomerRestService(Resource): """Handles REST operations for Customer /customer""" pass
class CustomerAddressRestService(Resource): """Handles REST operations for Customer Address /customer/<customerId>/address""" pass
Instead I would need to have one common Resource that handles everything under '/customer', including the customer entity and customer address entity in one (and any other entity whose root is '/customer').
That isn't a very good design...especially if you maybe have 50 different entities hooked up under '/customer' (like in our app). It seems in order to enable having nice cohesive classes that provide a REST service for just one entity I would need to manually route the request from the root Resource into each of them myself.
That is what i was trying to avoid, but it looks like there is no other choice.
Jacek

On 09:00 pm, jacek99@gmail.com wrote:
OK, so the issue I have is that I cannot create two separate REST Resources with nested URLs e.g.
class CustomerRestService(Resource): """Handles REST operations for Customer /customer"""
class CustomerAddressRestService(Resource): """Handles REST operations for Customer Address /customer/<customerId>/address""" pass
Instead I would need to have one common Resource that handles everything under '/customer', including the customer entity and customer address entity in one (and any other entity whose root is '/customer').
I don't understand what this means. It sounds like you're saying you have to have CustomerRestService be a leaf resource. You don't have to. If you don't want it to be a leaf resource, don't make it a leaf resource. The way you not making it a leaf resource is by not setting `isLeaf = True` on it.
That isn't a very good design...especially if you maybe have 50 different entities hooked up under '/customer' (like in our app). It seems in order to enable having nice cohesive classes that provide a REST service for just one entity I would need to manually route the request from the root Resource into each of them myself.
That is what i was trying to avoid, but it looks like there is no other choice.
I still don't understand the problem you're having. As I said, regarding the "solution" I gave in my previous email, it's just how you would achieve the behavior I thought maybe you were trying to describe wanting, but I don't really understand what you're trying to accomplish or why.
Jean-Paul

OK, then I am missing something utterly about how Leaf vs non-Leaf resources work.
Would you code the response to a render_GET in a Leaf resource only?
Or can you do it a non-Leaf resource as well?
If yes, how do you handle responding in certain scenarios (depending on URL path) in a non-Leaf Resource vs sending down the request to one of its child Leaf resources?
Jacek

On Wed, Feb 29, 2012 at 23:00, Jacek Furmankiewicz jacek99@gmail.com wrote:
OK, so the issue I have is that I cannot create two separate REST Resources with nested URLs e.g.
class CustomerRestService(Resource): """Handles REST operations for Customer /customer""" pass
class CustomerAddressRestService(Resource): """Handles REST operations for Customer Address /customer/<customerId>/address""" pass
Instead I would need to have one common Resource that handles everything under '/customer', including the customer entity and customer address entity in one (and any other entity whose root is '/customer').
Perhaps I'm misunderstanding something here, but the idea with Twisted resources (that aren't leaf resources) is not to have two things handle the same path. The way the resource lookups work is that Site loops through request.postpath from the root resource, locating child resources one segment at a time, until it either runs out of path segments *or* encounters a resource with isLeaf=True, that resource is then rendered.
So in this example you might implement a CustomerIndexResource (that exists at /customers) with a getChild method (/customers/<name>) that creates a CustomerResource with some information (via a database call, web service call, etc.). That CustomerResource then handles child resource location for things like "address" (/customers/<name>/address). This approach also makes it easier to do things like swap out CustomerResource for ReadOnlyCustomerResource that perhaps doesn't allow POST/PUT methods or prevents access to certain child resources by not even having those child resources.
That isn't a very good design...especially if you maybe have 50 different entities hooked up under '/customer' (like in our app). It seems in order to enable having nice cohesive classes that provide a REST service for just one entity I would need to manually route the request from the root Resource into each of them myself.
You can easily design a plugin system or other programmatic method for handling dynamic child dispatch for these 50 methods on a customer object.
Hope this helps.

That is exactly what I wanted to do (i.e. custom dispatching to child classes from a single leaf Resource).
I just wasn't sure if that was the right approach. Thanks for validating my approach. Sorry if my initial question didn't really explain it clearly enough.
Cheers Jacek

On 1 Mar, 02:59 pm, jacek99@gmail.com wrote:
That is exactly what I wanted to do (i.e. custom dispatching to child classes from a single leaf Resource).
This sounds like the opposite conclusion as Jonathan (and earlier, me) was suggesting. However, you trimmed *all* context from this reply, so perhaps I am not correctly interpreting you.
Anyway, good luck.
Jean-Paul

Well, maybe this will explain what I want to achieve, based on latest (already working) code from trunk:
A sample of a REST app with two REST services exposed
https://github.com/jacek99/corepost/blob/master/corepost/test/rest_resource....
There is a generic RESTResource (an actual twisted.web Resource) that register child REST services (just regular classes). It takes care of routing all the requests to them.
A human readable BDD that shows the unit tests for it: https://github.com/jacek99/corepost/blob/master/corepost/test/feature/rest_a...
I am getting pretty happy with this API. It achieves what I wanted, needs more thorough testing though before I release an official version with it.
Cheers Jacek
On Fri, Mar 2, 2012 at 9:18 AM, exarkun@twistedmatrix.com wrote:
On 1 Mar, 02:59 pm, jacek99@gmail.com wrote:
That is exactly what I wanted to do (i.e. custom dispatching to child classes from a single leaf Resource).
This sounds like the opposite conclusion as Jonathan (and earlier, me) was suggesting. However, you trimmed *all* context from this reply, so perhaps I am not correctly interpreting you.
Anyway, good luck.
Jean-Paul
Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web
participants (3)
-
exarkun@twistedmatrix.com
-
Jacek Furmankiewicz
-
Jonathan Jacobs