← Back to team overview

ladon-dev-team team mailing list archive

Re: REST implementation

 

Initially I was supposed to implement in some kind of way, but just using
the class-level decorator. But! It is too strict and not as flexible as
could be.
I told already taht I imagine that exposing RESTful methods might be a bit
more flexible.  Let's imagine that I already have some SOAP service which
is easily could map to REST by exposing the methods (assuming that SOAP
service is "almost RESTful", but all I need is to expose to HTTP GET not a
single method but several). You can look at, for example, JAX-WS - the REST
implementation is something like in my proposal. You put TOO strict
constraints for the REST service, mine is more flexible. I propose to
developer to decide what does he want to do with his service (he might want
to have 2 POST, 5 GET, 1 PUT and no DELETE HTTP methods exposed for
concrete service object). This mapping could be easily done by matching the
arguments passed through an HTTP, so the rule is a following:
 1. If you have several methods exposed to the same type of HTTP request,
which method to call is determined by matching the bypassed HTTP params and
the service method arguments
 2. REST is not RPC, so you can not tell which method to call by name, but
we can call different methods relying on their signatures

1,2 - the same but with different words.

Regards,
Mike

2012/10/23 Jakob Simon-Gaarde <jakobsg@xxxxxxxxx>

> Let's try to define the end-user result, and then discuss the
> implementation afterwards.
>
> In the following I have defined a very simple type as a case we can
> collaborate through.
>
> Maybe we have an object called Client that needs to be RESTful. I think
> the nicest thing to do would be to SubClass LadonType (ie.
> RESTfulLadonType) which has some virtual methods:
>
> class Client(RESTfulLadonType):
>
>   uid = PORTABLE_STRING
>   name = PORTABLE_STRING
>   account = PORTABLE_STRING
>   credit = bool
>
>   def get(self,id):
>     pass
>
>   def put(self,id):
>     pass
>
>   def delete(self,id):
>     pass
>
> Ladon sees right away that some og the objects used in the Service class
> are RESTful and adds restful to the list of protocols.
> Your turn!
>
> / Jakob
>
>
>
> 2012/10/23 Jakob Simon-Gaarde <jakobsg@xxxxxxxxx>
>
>> I knew this already :-) And that's not nessesarilly a bad thing,
>> depending on what you are developing. I just think we need to think through
>> how to implement it in Ladon. Since you have enjoyed using Ladon until now,
>> you probably already know that I am a hugh fan of end-user simplicity.
>>
>> And since REST is strictly object-oriented, I really think we should be
>> looking at decorating the LadonType instead of methods. I haven't thought
>> it all the way through yet, but I'm almost sure that would be the place to
>> do it.
>>
>>
>> / Jakob
>>
>>
>> 2012/10/23 Mykhailo Stadnyk <mikhus@xxxxxxxxx>
>>
>>> Exacly! REST is a constraint to objects by design! You've caught it. But
>>> it's good principal for data-oriented services with no overhead by any
>>> top-level protocol. It's a principal of building RESTful services. And will
>>> be good if Ladon will support it
>>>
>>> 2012/10/23 Jakob Simon-Gaarde <jakobsg@xxxxxxxxx>
>>>
>>>>  But, I think I know what you mean. It's the fact that it utilizes all
>>>> HTTP methods that makes you say that REST is in the HTTP protocol. I just
>>>> don't nessesarily find that this is very revolutionary. It's more of a
>>>> constraint, cause you are limited to objects by design.
>>>>
>>>> / Jakob
>>>>
>>>>
>>>> 2012/10/23 Mykhailo Stadnyk <mikhus@xxxxxxxxx>
>>>>
>>>>> For REST HTTP is not underlying level but the protocol itself. In
>>>>> terms of REST design of service is known, so all you need is just to
>>>>> describe a list of objects. And then for each object there is 4 operations
>>>>> you are able to perform Create (HTTP PUT), Read (HTTP GET), Update (HTTP
>>>>> POST) and Delete (HTTP DELETE). That's why in terms of REST you do not need
>>>>> the description. You have only URLs each representing the object you can do
>>>>> direct HTTP requests and you have no overhead of having some top-level
>>>>> protocol, etc. That's why I said that REST is HTTP, because there is
>>>>> nothing on top of HTTP in REST. And there is nothing to describe, only the
>>>>> object URLs.
>>>>>
>>>>> It means that RESTful service architecture is not as flexible as SOAP,
>>>>> so if you, as developer decided to create RESTful service it means that you
>>>>> already chosen the service architecture. It also means that not every SOAP
>>>>> service can be RESTful, but RESTful service could be delivered with SOAP.
>>>>> So you, as developer MUST decide first if you are building a RESTful
>>>>> service, if so - go ahead, make it and Ladon give you benefit to deliver
>>>>> your RESTful service throug varius different supported protocols.
>>>>>
>>>>> Again. You CAN deliver RESTful service throug
>>>>> SOAP/XML-RPC/JSON-WSP/etc. But you CAN NOT deliver abstract
>>>>> SOAP/XML-RPC/JSON-WSP service through REST if it wasn't designed as RESTful
>>>>>
>>>>> In my implementation I've tried to make everything with maximum
>>>>> flexibility, so each RESTFul service could have more than 4 CRUD methods,
>>>>> for each HTTP request method you can have as many service methods as you
>>>>> need if the service methods have different signatures (arguments)
>>>>>
>>>>> like
>>>>>
>>>>> class Service:
>>>>>     @restfulize(method="GET")
>>>>>     def get_one_record( r_id):
>>>>>          pass
>>>>>     @restfulize(method="GET")
>>>>>     def get_last_record( r_time):
>>>>>          pass
>>>>>     @restfulize(method="GET")
>>>>>     def get_all_records():
>>>>>          pass
>>>>>     @restfulize(method="GET")
>>>>>     def get_filtered_records( filters):
>>>>>          pass
>>>>>
>>>>> As you see in this example 4 service methods are bind to HTTP GET
>>>>> method. Depending of parameters bypassed through HTTP I try to determine
>>>>> which method to call, but from terms of protocol you always do
>>>>>
>>>>> GET
>>>>> /Service/rest?filters['name']=*bla&filters['another_key']=somevalue HTTP/1.1
>>>>> Host: yourservice.com
>>>>> ...
>>>>>
>>>>> The same for other HTTP methods. You can have 2 different methods for
>>>>> POST, for example for User service example you may have split User data
>>>>> update and user password update, etc..
>>>>>
>>>>> Or maybe your service may not provide some methods. Like you create
>>>>> the public read-only service, so you don't provide PUT/POST/DELETE
>>>>>
>>>>> P.S. REST does not provide the way do bypass which service method to
>>>>> call by name. REST is not RPC. You have only the Resource, it's URL and 4
>>>>> operations. It is REST.
>>>>>
>>>>> Best regards,
>>>>> Mike
>>>>>
>>>>>
>>>>> 2012/10/23 Jakob Simon-Gaarde <jakobsg@xxxxxxxxx>
>>>>>
>>>>>> Everything in Ladon is HTTP :-)
>>>>>>
>>>>>> But HTTP is just the underlying protocol, and under that is a
>>>>>> transport protocol etc.
>>>>>>
>>>>>> When I am talking about protocol in Ladon I mean service protocols
>>>>>> like soap, json-wsp and soon json-rpc which are also protocols. I would put
>>>>>> REST at this level aswell.
>>>>>>
>>>>>> I haven't really used REST before, but from what I can see it is
>>>>>> nothing more than another service protocol which utilizes the HTTP standard
>>>>>> a bit more, like using PUT,POST,GET,DELETE methods. Also it looks like
>>>>>> there is not so much constraint on how parameters are passed so they can be
>>>>>> JSON or XML or query-string.
>>>>>>
>>>>>> I don't like that there is no service description format for REST
>>>>>> services. That is kind of a key feature in Ladon, that all supported
>>>>>> protocols should be able to describe themselves.
>>>>>>
>>>>>>
>>>>>> / Jakob
>>>>>>
>>>>>> 2012/10/22 Mykhailo Stadnyk <mikhus@xxxxxxxxx>
>>>>>>
>>>>>>> What do you mean?
>>>>>>>
>>>>>>> Actually REST is HTTP in terms of protocol, so what do I need to
>>>>>>> implement? Maybe you can explain me more detailed your thought.
>>>>>>>
>>>>>>> Best regards,
>>>>>>> Mike
>>>>>>>
>>>>>>> 2012/10/22 Jakob Simon-Gaarde <jakobsg@xxxxxxxxx>
>>>>>>>
>>>>>>>> My first question about your RESTful implementation is, why are you
>>>>>>>> not implementing it as a protocol?
>>>>>>>>
>>>>>>>> / Jakob
>>>>>>>>
>>>>>>>> 2012/10/22 Jakob Simon-Gaarde <jakobsg@xxxxxxxxx>
>>>>>>>>
>>>>>>>>> Just a remark about the package name tracepyd. That might be an
>>>>>>>>> unfortunate package name, cause on that odd platform called Windows
>>>>>>>>> pyd-files are binary python modules. Just a remark :-)
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> 2012/10/22 Jakob Simon-Gaarde <jakobsg@xxxxxxxxx>
>>>>>>>>>
>>>>>>>>>> Hi Mike.
>>>>>>>>>>
>>>>>>>>>> Let's talk about tracepyd first, does it solve this bug:
>>>>>>>>>>
>>>>>>>>>> https://bugs.launchpad.net/ladon/+bug/877727
>>>>>>>>>>
>>>>>>>>>> rpclib has a terribly ugly solution for this, as you can see in
>>>>>>>>>> the bug, and we want to be better than rpclib :-)
>>>>>>>>>>
>>>>>>>>>> / Jakob
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> 2012/10/22 Jakob Simon-Gaarde <jakobsg@xxxxxxxxx>
>>>>>>>>>>
>>>>>>>>>>> Hi Mike
>>>>>>>>>>>
>>>>>>>>>>> Looking into this now, you will have my reply today :-)
>>>>>>>>>>>
>>>>>>>>>>> / Jakob
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> 2012/10/22 Mykhailo Stadnyk <mikhus@xxxxxxxxx>
>>>>>>>>>>>
>>>>>>>>>>>> Currently it's an experimental feature which is not ready to be
>>>>>>>>>>>> integrated. I didn't test it at all (keep working on it).
>>>>>>>>>>>>
>>>>>>>>>>>> So here is my personal road-map for this feature:
>>>>>>>>>>>>   1. Resolve problem with decorators dependencies (something I
>>>>>>>>>>>> described in previous message)
>>>>>>>>>>>>   2. Write tests and test the functionality
>>>>>>>>>>>>   3. I've not checked how does it deal with requests containing
>>>>>>>>>>>> multipart boundaries and binary data
>>>>>>>>>>>>   4. Not sure what to do with service description (currently
>>>>>>>>>>>> I've just removed, but have an idea we can use it to describe service any
>>>>>>>>>>>> way, even as long as REST does not specify this)
>>>>>>>>>>>>   5. Write documentation/examples
>>>>>>>>>>>>
>>>>>>>>>>>> But I'm stuck on issue #1. I want to know:
>>>>>>>>>>>>  * do you agree to add dependency in Ladon to tracepyd module
>>>>>>>>>>>> or not?
>>>>>>>>>>>>  * If not - do you agree to include tracepyd's implementation
>>>>>>>>>>>> into Ladon (not as dependency, but to place it somewhere in tools)?
>>>>>>>>>>>>  * If not - do you agree to modify @ladonize decorator
>>>>>>>>>>>> functionality to add extra-params for REST (for me its ugly).
>>>>>>>>>>>>  * If not - which ideas do you have then? :)
>>>>>>>>>>>>
>>>>>>>>>>>> I'd like to suggest to add dependency to tracepyd. Currently I
>>>>>>>>>>>> have a version locally which works fine, so I can publish this solution to
>>>>>>>>>>>> my branch to let you see.
>>>>>>>>>>>>
>>>>>>>>>>>> Thank you in advance for your time and advice,
>>>>>>>>>>>> Mike
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> 2012/10/22 Jakob Simon-Gaarde <jakobsg@xxxxxxxxx>
>>>>>>>>>>>>
>>>>>>>>>>>>> Interesting :-)
>>>>>>>>>>>>>
>>>>>>>>>>>>> I'll look at it later - we must make sure that it doesn't
>>>>>>>>>>>>> break anything.
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> 2012/10/22 Mykhailo Stadnyk <mikhus@xxxxxxxxx>
>>>>>>>>>>>>>
>>>>>>>>>>>>>> Hi Jacob.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> As I told I'm trying to implement RESTful services support by
>>>>>>>>>>>>>> Ladon. The basic functionality is implemented - you can check my branch:
>>>>>>>>>>>>>> lp:~mikhus/ladon/rest<https://code.launchpad.net/~mikhus/ladon/rest> to
>>>>>>>>>>>>>> see how does it work. Here is an example of RESTful service you may run to
>>>>>>>>>>>>>> see:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> from ladon.ladonizer import ladonize, restfulize
>>>>>>>>>>>>>> from ladon.compat import PORTABLE_STRING
>>>>>>>>>>>>>> from ladon.types.ladontype import LadonType
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> class User(LadonType):
>>>>>>>>>>>>>>  name = PORTABLE_STRING
>>>>>>>>>>>>>> email = PORTABLE_STRING
>>>>>>>>>>>>>> passwd = PORTABLE_STRING
>>>>>>>>>>>>>>  def __init__():
>>>>>>>>>>>>>>  self.name = ""
>>>>>>>>>>>>>>  self.email = ""
>>>>>>>>>>>>>>  self.passwd = ""
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> class UserService( object) :
>>>>>>>>>>>>>>  @restfulize(method="PUT")
>>>>>>>>>>>>>>  @ladonize(str, str, str, rtype=User)
>>>>>>>>>>>>>> def newUser( self, u_name, u_email, u_passwd) :
>>>>>>>>>>>>>>  user = User() # where User is LadonType object
>>>>>>>>>>>>>> user.name   = u_name
>>>>>>>>>>>>>>  user.email  = u_email
>>>>>>>>>>>>>> user.passwd = u_passwd
>>>>>>>>>>>>>> return user
>>>>>>>>>>>>>>  @restfulize(method="GET")
>>>>>>>>>>>>>> @ladonize(int, rtype=[User])
>>>>>>>>>>>>>>  def getUser(self, u_id) :
>>>>>>>>>>>>>> return User()
>>>>>>>>>>>>>>  @restfulize(method="GET")
>>>>>>>>>>>>>> @ladonize(int, rtype=[User])
>>>>>>>>>>>>>> def getUsers(self) :
>>>>>>>>>>>>>>  return [User(), User(), User()]
>>>>>>>>>>>>>>  @restfulize(method="POST")
>>>>>>>>>>>>>>  @ladonize(int, str, str, str, rtype=User)
>>>>>>>>>>>>>> def updUser( self, u_id, u_name, u_email, u_passwd):
>>>>>>>>>>>>>>  user = User()
>>>>>>>>>>>>>> user.name   = u_name
>>>>>>>>>>>>>> user.email  = u_email
>>>>>>>>>>>>>>  user.passwd = u_passwd
>>>>>>>>>>>>>> return user
>>>>>>>>>>>>>>  @restfulize(method="DELETE")
>>>>>>>>>>>>>> @ladonize(int, rtype=bool)
>>>>>>>>>>>>>> def delUser( self, u_id) :
>>>>>>>>>>>>>>  return True
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> By the way, there is at least one issue I don't know how to
>>>>>>>>>>>>>> solve better, so I want your advice.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> As you see I've implemented specific decorator to mark
>>>>>>>>>>>>>> service methods as RESTful. It's @restfulize decorator. The problem that it
>>>>>>>>>>>>>> is implemented now as:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> def restfulize( *dargs, **dkwargs):
>>>>>>>>>>>>>>  def decorator(fn):
>>>>>>>>>>>>>> * minfo = fn._ladon_method_info*
>>>>>>>>>>>>>> if 'method' in dkwargs:
>>>>>>>>>>>>>>  if not minfo.restfulize( **dkwargs):
>>>>>>>>>>>>>> raise RestfulMethodConfigMismatch( fn.func_name)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> return fn # returns ladonized method
>>>>>>>>>>>>>> return decorator
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The problem is highlighted with bold. Will be good to make
>>>>>>>>>>>>>> this decorator independent of what @ladonize defined in it's private props.
>>>>>>>>>>>>>> But, as far as "fn" here is a reference to ladon's "injector" function I
>>>>>>>>>>>>>> have no way to determine correctly which real method in which class is
>>>>>>>>>>>>>> decorated. It means that in such case I have no way to extract
>>>>>>>>>>>>>> LadonMethodInfo associated to "fn" in other way but implemented for the
>>>>>>>>>>>>>> moment.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> The real problem is when I want to override @ladonize
>>>>>>>>>>>>>> decorator. In this case @resftulize becomes nonoperational because I loose
>>>>>>>>>>>>>> reference to "_ladon_method_info" and means that I should take care to
>>>>>>>>>>>>>> bypass all the existing private properties in 3-d party override
>>>>>>>>>>>>>> implementation, which is not good at all. So I want to have @restfulize and
>>>>>>>>>>>>>> @ladonize implemented without hard dependency between them.
>>>>>>>>>>>>>> Via global_service_collection() it's possible to do but you
>>>>>>>>>>>>>> need to determine real method function in some way.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> So I see the following ways to fix it:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> 1. Via decorator registry (Here we solved such kind of
>>>>>>>>>>>>>> problem by creating DecoratorRegistry (I've published it as
>>>>>>>>>>>>>> http://pypi.python.org/pypi/tracepyd))
>>>>>>>>>>>>>> 2. By adding REST functionality to @ladonize decorator
>>>>>>>>>>>>>> itself, to have something like:
>>>>>>>>>>>>>>     @ladonize(PORTABLE_STRING, rtype=[PORTABLE_STRING], *rest={
>>>>>>>>>>>>>> "method" : "GET", "produces": "XML" }*)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Maybe you can point me to some other ideas you know how to
>>>>>>>>>>>>>> resolve such kind of problem.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Best regards,
>>>>>>>>>>>>>> Mike
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> --
>>>>>>>>>>>>> Med venlig hilsen / Best regards
>>>>>>>>>>>>> Jakob Simon-Gaarde
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> --
>>>>>>>>>>> Med venlig hilsen / Best regards
>>>>>>>>>>> Jakob Simon-Gaarde
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> Med venlig hilsen / Best regards
>>>>>>>>>> Jakob Simon-Gaarde
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> --
>>>>>>>>> Med venlig hilsen / Best regards
>>>>>>>>> Jakob Simon-Gaarde
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> Med venlig hilsen / Best regards
>>>>>>>> Jakob Simon-Gaarde
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> Med venlig hilsen / Best regards
>>>>>> Jakob Simon-Gaarde
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> Med venlig hilsen / Best regards
>>>> Jakob Simon-Gaarde
>>>>
>>>> --
>>>> Mailing list: https://launchpad.net/~ladon-dev-team
>>>> Post to     : ladon-dev-team@xxxxxxxxxxxxxxxxxxx
>>>> Unsubscribe : https://launchpad.net/~ladon-dev-team
>>>> More help   : https://help.launchpad.net/ListHelp
>>>>
>>>>
>>>
>>
>>
>> --
>> Med venlig hilsen / Best regards
>> Jakob Simon-Gaarde
>>
>
>
>
> --
> Med venlig hilsen / Best regards
> Jakob Simon-Gaarde
>
> --
> Mailing list: https://launchpad.net/~ladon-dev-team
> Post to     : ladon-dev-team@xxxxxxxxxxxxxxxxxxx
> Unsubscribe : https://launchpad.net/~ladon-dev-team
> More help   : https://help.launchpad.net/ListHelp
>
>

Follow ups

References