Named operations

Entries and collections support named operations: one-off functionality that’s been given a name and a set of parameters.

>>> from lazr.restfulclient.tests.example import CookbookWebServiceClient
>>> service = CookbookWebServiceClient()

Arguments to named operations are automatically converted to JSON for transmission over the wire. Here’s a named operation that takes a boolean argument.

>>> [recipe for recipe in service.cookbooks.find_recipes(
...      search="Chicken", vegetarian=True)]
[]

Strings that happen to be numbers are handled properly. Here, if “1.234” were converted into a number at any point in the chain, the ‘search’ operation on the server wouldn’t know how to handle it and the request would fail.

>>> [people for people in service.cookbooks.find_recipes(search="1.234")]
[]

Counts

Named operations that return a collection return either a link that lazr.restfulclient can follow to get the size of the collection or (under some circumstances) the length itself. The len() function hides this indirection from the end-user.

>>> results = service.cookbooks.find_recipes(search="Chicken")
>>> print len(results)
0

Special data types

lazr.restfulclient uses some data types that don’t directly correspond to JSON data types. These data types can be used in named operations. For instance, a named operation can take a date or datetime object as one of its arguments.

>>> import datetime
>>> date = datetime.datetime(1994, 1, 1)
>>> cookbook = service.cookbooks.create(
...     name="New cookbook", cuisine="General",
...     copyright_date=date, price=1.23, last_printing=date)
>>> print cookbook.name
New cookbook

A named operation can take an entry as one of its arguments. lazr.restfulclient lets you pass in the actual entry as the argument value.

>>> dish = service.recipes[1].dish
>>> cookbook = service.recipes[1].cookbook
>>> print cookbook.find_recipe_for(dish=dish)
http://cookbooks.dev/1.0/recipes/1

A named operation can take binary data as one of its arguments.

>>> cookbook.replace_cover(cover="\x00\xe2\xe3")
>>> cookbook.cover.open().read()
'\x00\xe2\xe3'

A named operation that returns a null value corresponds to a Python value of None.

>>> dish = service.recipes[4].dish
>>> print cookbook.find_recipe_for(dish=dish)
None

A named operation may change the resource’s location so we get a 301 response with the new URL.

>>> from urllib import quote
>>> from lazr.restful.testing.webservice import WebServiceCaller
>>> webservice = WebServiceCaller(domain='cookbooks.dev')
>>> url = quote("/cookbooks/New cookbook")
>>> print webservice.named_post(url, 'make_more_interesting')
HTTP/1.1 301 Moved Permanently
...
Location: http://cookbooks.dev/devel/cookbooks/The%20New%20New%20cookbook
...

JSON-encoding

lazr.restfulclient encodes most arguments (even string arguments) as JSON before sending them over the wire. This way, a named operation that takes a string argument can take a string that looks like a JSON object without causing confusion.

>>> cookbooks = service.cookbooks.find_for_cuisine(cuisine="General")
>>> len([cookbook for cookbook in cookbooks]) > 0
True
>>> cookbook = service.cookbooks.create(
...    name="null", cuisine="General",
...    copyright_date=date, price=1.23, last_printing=date)
>>> cookbook.name
u'null'
>>> cookbook = service.cookbooks.create(
...     name="4.56", cuisine="General",
...     copyright_date=date, price=1.23, last_printing=date)
>>> cookbook.name
u'4.56'
>>> cookbook = service.cookbooks.create(
...     name='"foo"', cuisine="General",
...     copyright_date=date, price=1.23, last_printing=date)
>>> cookbook.name
u'"foo"'

A named operation that takes a non-string object (such as a float) will not accept a string that’s the JSON representation of the object.

>>> try:
...     service.cookbooks.create(
...         name="Yet another 1.23 cookbook", cuisine="General",
...         copyright_date=date, last_printing=date, price="1.23")
... except Exception, e:
...     print e.content
price: got 'unicode', expected float, int: u'1.23'

Named operations on collections don’t fetch the collections

If you invoke a named operation on a collection, the only HTTP request made is the one for the named operation. You don’t have to get a representation of the collection to invoke the operation.

>>> import httplib2
>>> httplib2.debuglevel = 1
>>> service = CookbookWebServiceClient()
send: ...
...
>>> print service.cookbooks.find_recipes(
...      search="Chicken", vegetarian=True)
send: 'GET /1.0/cookbooks?...vegetarian=true...'
...

Cleanup.

>>> httplib2.debuglevel = None