Merge lp:~leonardr/lazr.restful/474522-type-error into lp:lazr.restful

Proposed by Leonard Richardson
Status: Merged
Merged at revision: 149
Proposed branch: lp:~leonardr/lazr.restful/474522-type-error
Merge into: lp:lazr.restful
Diff against target: 191 lines (+99/-15)
5 files modified
src/lazr/restful/NEWS.txt (+7/-0)
src/lazr/restful/example/base/tests/entry.txt (+65/-9)
src/lazr/restful/example/base/tests/redirect.txt (+1/-0)
src/lazr/restful/marshallers.py (+25/-5)
src/lazr/restful/version.txt (+1/-1)
To merge this branch: bzr merge lp:~leonardr/lazr.restful/474522-type-error
Reviewer Review Type Date Requested Status
Edwin Grubbs (community) code Approve
Review via email: mp+37018@code.launchpad.net

Description of the change

Ignore the name of this branch; it refers to a backlog bug that turned out to be invalid.

This branch fixes a different backlog bug, bug 497602. It makes lazr.restful accept relative URLs when dereferencing URLs. This happens when you pass a URL into a named operation or try to change an entry's link. The URLs are relative to the versioned service root (so "/foo" might actually be "http://server/1.0/foo").

I also updated some imports and fixed a trivial test failure in the previous revision.

To post a comment you must log in.
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

Hi Leonard,

This branch looks great. Thanks for fixing this issue.

-Edwin

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/lazr/restful/NEWS.txt'
--- src/lazr/restful/NEWS.txt 2010-09-27 18:31:14 +0000
+++ src/lazr/restful/NEWS.txt 2010-09-29 16:47:51 +0000
@@ -2,6 +2,13 @@
2NEWS for lazr.restful2NEWS for lazr.restful
3=====================3=====================
44
50.13.3 (2010-09-29)
6===================
7
8Named operations that take URLs as arguments will now accept URLs
9relative to the versioned service root. Previously they would only
10accept absolute URLs. This fixes bug 497602.
11
50.13.2 (2010-09-27)120.13.2 (2010-09-27)
6===================13===================
714
815
=== modified file 'src/lazr/restful/example/base/tests/entry.txt'
--- src/lazr/restful/example/base/tests/entry.txt 2010-08-26 18:22:19 +0000
+++ src/lazr/restful/example/base/tests/entry.txt 2010-09-29 16:47:51 +0000
@@ -222,11 +222,33 @@
222object. Here the 'dish' argument is the URL to a dish, and the named222object. Here the 'dish' argument is the URL to a dish, and the named
223operation finds a recipe for making that dish.223operation finds a recipe for making that dish.
224224
225 >>> def find_recipe_in_joy(dish_url):
226 ... """Look up a dish in 'The Joy of Cooking'."""
227 ... return webservice.get("%s?ws.op=find_recipe_for&dish=%s" %
228 ... (joy_url, quote(dish_url))).jsonBody()
229
225 >>> dish_url = webservice.get("/recipes/2").jsonBody()['dish_link']230 >>> dish_url = webservice.get("/recipes/2").jsonBody()['dish_link']
226 >>> recipe = webservice.get("%s?ws.op=find_recipe_for&dish=%s" %231 >>> find_recipe_in_joy(dish_url)['instructions']
227 ... (joy_url, quote(dish_url))).jsonBody()232 u'Draw, singe, stuff, and truss...'
228 >>> recipe['instructions']233
229 u'Draw, singe, stuff, and truss...'234The URL passed in to a named operation may be an absolute URL, or it
235may be relative to the versioned service root. This is for developer
236convenience only, as lazr.restful never serves relative URLs.
237
238 >>> print dish_url
239 http://cookbooks.dev/devel/dishes/Roast%20chicken
240 >>> relative_url = quote("/dishes/Roast chicken")
241 >>> find_recipe_in_joy(relative_url)['instructions']
242 u'Draw, singe, stuff, and truss...'
243
244A URL relative to the unversioned service root will not work.
245
246 >>> relative_url = quote("/devel/dishes/Roast chicken")
247 >>> find_recipe_in_joy(relative_url)
248 Traceback (most recent call last):
249 ...
250 ValueError: dish: No such object "/devel/dishes/Roast%20chicken".
251
230252
231Some entries support custom operations through POST. You can invoke a253Some entries support custom operations through POST. You can invoke a
232custom operation to modify a cookbook's name, making it seem more254custom operation to modify a cookbook's name, making it seem more
@@ -274,14 +296,19 @@
274should use the PATCH HTTP method. Or it may completely describe the296should use the PATCH HTTP method. Or it may completely describe the
275entry's state, in which case the client should use PUT.297entry's state, in which case the client should use PUT.
276298
277 >>> def modify_cookbook(cookbook, representation, method, headers=None):299 >>> def modify_entry(url, representation, method, headers=None):
278 ... "A helper function to PUT or PATCH a cookbook."300 ... "A helper function to PUT or PATCH an entry."
279 ... new_headers = {'Content-type': 'application/json'}301 ... new_headers = {'Content-type': 'application/json'}
280 ... if headers is not None:302 ... if headers is not None:
281 ... new_headers.update(headers)303 ... new_headers.update(headers)
282 ... return webservice('/cookbooks/' + quote(cookbook), method,304 ... return webservice(
283 ... simplejson.dumps(representation),305 ... url, method, simplejson.dumps(representation), headers)
284 ... headers)306
307 >>> def modify_cookbook(cookbook, representation, method, headers=None):
308 ... "A helper function to PUT or PATCH a cookbook."
309 ... return modify_entry(
310 ... '/cookbooks/' + quote(cookbook), representation,
311 ... method, headers)
285312
286Here we use the web service to change the cuisine of the "Everyday313Here we use the web service to change the cuisine of the "Everyday
287Greens" cookbook. The data returned is the new JSON representation of314Greens" cookbook. The data returned is the new JSON representation of
@@ -309,6 +336,35 @@
309 >>> print greens['revision_number']336 >>> print greens['revision_number']
310 1337 1
311338
339A modification may cause one of en entry's links to point to another
340object. Here, we change the 'dish_link' field of a roast chicken
341recipe, turning it into a recipe for baked beans.
342
343 >>> old_dish = webservice.get("/recipes/1").jsonBody()['dish_link']
344 >>> print old_dish
345 http://.../dishes/Roast%20chicken
346
347 >>> new_dish = webservice.get("/recipes/4").jsonBody()['dish_link']
348 >>> print new_dish
349 http://.../dishes/Baked%20beans
350
351 >>> new_entry = modify_entry(
352 ... "/recipes/2", {'dish_link' : new_dish}, 'PATCH').jsonBody()
353 >>> print new_entry['dish_link']
354 http://.../dishes/Baked%20beans
355
356When changing one of an entry's links, you can use an absolute URL (as
357seen above) or a URL relative to the versioned service root. Let's use
358a relative URL to change the baked beans recipe back to a roast
359chicken recipe.
360
361 >>> relative_old_dish = quote('/dishes/Roast chicken')
362 >>> new_entry = modify_entry(
363 ... "/recipes/2", {'dish_link' : relative_old_dish},
364 ... 'PATCH').jsonBody()
365 >>> print new_entry['dish_link']
366 http://.../dishes/Roast%20chicken
367
312A modification might cause an entry's address to change. Here we use368A modification might cause an entry's address to change. Here we use
313the web service to change the cookbook's name to 'Everyday Greens 2'.369the web service to change the cookbook's name to 'Everyday Greens 2'.
314370
315371
=== modified file 'src/lazr/restful/example/base/tests/redirect.txt'
--- src/lazr/restful/example/base/tests/redirect.txt 2010-09-27 16:40:28 +0000
+++ src/lazr/restful/example/base/tests/redirect.txt 2010-09-29 16:47:51 +0000
@@ -42,3 +42,4 @@
42 HTTP/1.1 301 Moved Permanently42 HTTP/1.1 301 Moved Permanently
43 ...43 ...
44 Location: http://.../Mastering%20the%20Art%20of%20French%20Cooking{invalid}?ws.accept=application/json44 Location: http://.../Mastering%20the%20Art%20of%20French%20Cooking{invalid}?ws.accept=application/json
45 ...
4546
=== modified file 'src/lazr/restful/marshallers.py'
--- src/lazr/restful/marshallers.py 2009-11-10 13:58:22 +0000
+++ src/lazr/restful/marshallers.py 2010-09-29 16:47:51 +0000
@@ -28,17 +28,30 @@
2828
29import simplejson29import simplejson
3030
31from zope.datetime import DateTimeError, DateTimeParser31from zope.datetime import (
32from zope.component import getMultiAdapter, getUtility32 DateTimeError,
33 DateTimeParser,
34 )
35from zope.component import (
36 getMultiAdapter,
37 getUtility,
38 )
33from zope.interface import implements39from zope.interface import implements
34from zope.publisher.interfaces import NotFound40from zope.publisher.interfaces import NotFound
35from zope.security.proxy import removeSecurityProxy41from zope.security.proxy import removeSecurityProxy
36from zope.traversing.browser import absoluteURL42from zope.traversing.browser import absoluteURL
3743
38from lazr.uri import URI, InvalidURIError44from lazr.uri import (
45 URI,
46 InvalidURIError,
47 )
3948
40from lazr.restful.interfaces import (49from lazr.restful.interfaces import (
41 IFieldMarshaller, IUnmarshallingDoesntNeedValue, IWebServiceConfiguration)50 IFieldMarshaller,
51 IUnmarshallingDoesntNeedValue,
52 IServiceRootResource,
53 IWebServiceConfiguration,
54 )
42from lazr.restful.utils import safe_hasattr55from lazr.restful.utils import safe_hasattr
4356
4457
@@ -74,7 +87,14 @@
74 if not isinstance(url, basestring):87 if not isinstance(url, basestring):
75 raise ValueError("got '%s', expected string: %r" % (88 raise ValueError("got '%s', expected string: %r" % (
76 type(url).__name__, url))89 type(url).__name__, url))
77 uri = URI(url)90 if url.startswith('/'):
91 # It's a relative URI. Resolve it relative to the root of this
92 # version of the web service.
93 service_root = getUtility(IServiceRootResource)
94 root_uri = absoluteURL(service_root, self.request)
95 uri = URI(root_uri).append(url[1:])
96 else:
97 uri = URI(url)
78 protocol = uri.scheme98 protocol = uri.scheme
79 host = uri.host99 host = uri.host
80 port = uri.port or default_port100 port = uri.port or default_port
81101
=== modified file 'src/lazr/restful/version.txt'
--- src/lazr/restful/version.txt 2010-09-27 18:31:14 +0000
+++ src/lazr/restful/version.txt 2010-09-29 16:47:51 +0000
@@ -1,1 +1,1 @@
10.13.210.13.3

Subscribers

People subscribed via source and target branches