Merge lp:~leonardr/wadllib/what-kind-of-link into lp:wadllib

Proposed by Leonard Richardson
Status: Merged
Approved by: Curtis Hovey
Approved revision: 23
Merged at revision: 20
Proposed branch: lp:~leonardr/wadllib/what-kind-of-link
Merge into: lp:wadllib
Diff against target: 259 lines (+117/-14)
4 files modified
src/wadllib/NEWS.txt (+10/-0)
src/wadllib/README.txt (+52/-8)
src/wadllib/application.py (+54/-5)
src/wadllib/version.txt (+1/-1)
To merge this branch: bzr merge lp:~leonardr/wadllib/what-kind-of-link
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
j.c.sackett (community) code* Approve
Review via email: mp+48485@code.launchpad.net

Description of the change

Currently, wadllib assumes that all <link>s are links to other WADL-controlled resources. If you try to follow a link that's just a random link to some URL, you'll get an exception. There is no easy way to examine a link beforehand to see what kind of link it is.

This branch adds the '.link' property to Parameter, which lets you get the Link object under consideration. It adds a '.can_follow' property to Link, which lets you see whether you can handle the link with wadllib or whether you need to use a general HTTP client. For convenience, it also adds a '.follow' property to Link.

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :
Download full text (3.2 KiB)

Leonard--

This looks like a good improvement.

I have two suggestions (not requirements):

> === modified file 'src/wadllib/README.txt'
> --- src/wadllib/README.txt 2010-05-03 15:58:32 +0000
> +++ src/wadllib/README.txt 2011-02-03 15:16:29 +0000
> @@ -274,6 +273,57 @@
> >>> new_team.type_url
> 'http://api.launchpad.dev/beta/#team'
>
> +Examining links
> +---------------
> +
> +The 'linked_resource' property of a parameter lets you follow a link
> +to another object. The 'link' property of a parameter lets you examine
> +links before following them.
> +
> + >>> import simplejson
> + >>> links_wadl = application_for('links-wadl.xml')
> + >>> service_root = links_wadl.get_resource_by_path('')
> + >>> representation = simplejson.dumps(
> + ... {'scalar_value': 'foo',
> + ... 'known_link': 'http://known/',
> + ... 'unknown_link': 'http://unknown/'})
> + >>> bound_root = service_root.bind(representation)
> +
> + >>> print bound_root.get_parameter("scalar_value").link
> + None
> +
> + >>> known_resource = bound_root.get_parameter("known_link")
> + >>> link_to_known_resource = known_resource.link
> + >>> unknown_resource = bound_root.get_parameter("unknown_link")
> + >>> link_to_unknown_resource = unknown_resource.link
> +
> + >>> print link_to_known_resource.can_follow
> + True
> + >>> print link_to_unknown_resource.can_follow
> + False

This might read easier without assigning the .link to a variable. e.g.

+ >>> known_resource = bound_root.get_parameter("known_link")
+ >>> unknown_resource = bound_root.get_parameter("unknown_link")
+
+ >>> print known_resource.link.can_follow
+ True
+ >>> print unknown_resource.link.can_follow

I only suggest this because I think it's a clearer indication of using the "link" property, which is the ostensible point of this section of the doc test.

>=== modified file 'src/wadllib/application.py'
>--- src/wadllib/application.py 2010-05-03 16:15:19 +0000
>+++ src/wadllib/application.py 2011-02-03 15:16:29 +0000
>@@ -962,6 +976,28 @@
> self.parameter = parameter
> self.tag = link_tag
>
>+ @property
>+ def follow(self):
>+ """Follow the link to another Resource."""
>+ if not self.can_follow:
>+ raise WADLError("Cannot follow a link when the target has no "
>+ "WADL description. Try using a general HTTP "
>+ "client instead.")
>+ return self.resolve_definition()
>+
>+ @property
>+ def can_follow(self):
>+ """Can this link be followed within wadllib?
>+
>+ wadllib can follow a link if it points to a resource that has
>+ a WADL definition.
>+ """
>+ try:
>+ definition_url = self._get_definition_url()
>+ return True
>+ except WADLError:
>+ return False

It might make a bit more sense to return True as the last statement of this property, rather than returning True in the "try" clause. i.e.:

>+ try:
>+ definition_url = self._get_definition_url()
>+ except WADLError:
>+ return False
>+ return True

Min...

Read more...

review: Approve (code*)
20. By Leonard Richardson

This is actually version 1.1.9. Now trying to merge in version 1.1.8, which was never merged with trunk.

21. By Leonard Richardson

Merge from trunk.

22. By Leonard Richardson

Minor changes in response to feedback.

23. By Leonard Richardson

Added the 'parameters' method.

Revision history for this message
Curtis Hovey (sinzui) wrote :

This looks good to land.

review: Approve (code)
Revision history for this message
Francis J. Lacoste (flacoste) wrote :

Turns out that the file "links-wadl.xml" wasn't added to the branch :-(

I'll try to reconstruct it from the test, or will remove the test.

Revision history for this message
Francis J. Lacoste (flacoste) wrote :

That seems to do the trick:

=== added file 'src/wadllib/tests/data/links-wadl.xml'
--- src/wadllib/tests/data/links-wadl.xml 1970-01-01 00:00:00 +0000
+++ src/wadllib/tests/data/links-wadl.xml 2011-11-25 18:53:12 +0000
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://research.sun.com/wadl/2006/10"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xsi:schemaLocation="http://research.sun.com/wadl/2006/10/wadl.xsd">
+ <resources base="http://www.example.com/">
+ <resource path="" type="#service-root"/>
+ </resources>
+
+ <resource_type id="service-root">
+ <method name="GET" id="service-root-get">
+ <response>
+ <representation href="#service-root-json"/>
+ </response>
+ </method>
+ </resource_type>
+
+ <representation id="service-root-json" mediaType="application/json">
+ <param style="plain" path="$['scalar_value']" name="scalar_value" />
+ <param style="plain" path="$['known_link']" name="known_link">
+ <link resource_type="#service-root"/>
+ </param>
+ <param style="plain" path="$['unknown_link']" name="unknown_link">
+ <link />
+ </param>
+ </representation>
+</application>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/wadllib/NEWS.txt'
2--- src/wadllib/NEWS.txt 2011-02-03 15:46:06 +0000
3+++ src/wadllib/NEWS.txt 2011-02-03 16:29:49 +0000
4@@ -2,6 +2,16 @@
5 NEWS for wadllib
6 ================
7
8+1.2.0 (2011-02-03)
9+==================
10+
11+- It's now possible to examine a link before following it, to see
12+ whether it has a WADL description or whether it needs to be fetched
13+ with a general HTTP client.
14+
15+- It's now possible to iterate over a resource's Parameter objects
16+ with the .parameters() method.
17+
18 1.1.8 (2010-10-27)
19 ==================
20
21
22=== modified file 'src/wadllib/README.txt'
23--- src/wadllib/README.txt 2010-05-03 15:58:32 +0000
24+++ src/wadllib/README.txt 2011-02-03 16:29:49 +0000
25@@ -43,7 +43,6 @@
26 ... "http://api.launchpad.dev/beta/")
27
28
29-===============
30 Link navigation
31 ===============
32
33@@ -139,12 +138,13 @@
34 test data.
35
36 >>> bound_service_root = bind_to_testdata(service_root, 'root')
37+ >>> sorted([param.name for param in bound_service_root.parameters()])
38+ ['bugs_collection_link', 'people_collection_link']
39 >>> sorted(bound_service_root.parameter_names())
40 ['bugs_collection_link', 'people_collection_link']
41 >>> [method.id for method in bound_service_root.method_iter]
42 ['service-root-get']
43
44-
45 Now the bound resource object has a JSON representation, and now
46 'people_collection_link' makes sense. We can follow the
47 'people_collection_link' to a new Resource object.
48@@ -274,6 +274,55 @@
49 >>> new_team.type_url
50 'http://api.launchpad.dev/beta/#team'
51
52+Examining links
53+---------------
54+
55+The 'linked_resource' property of a parameter lets you follow a link
56+to another object. The 'link' property of a parameter lets you examine
57+links before following them.
58+
59+ >>> import simplejson
60+ >>> links_wadl = application_for('links-wadl.xml')
61+ >>> service_root = links_wadl.get_resource_by_path('')
62+ >>> representation = simplejson.dumps(
63+ ... {'scalar_value': 'foo',
64+ ... 'known_link': 'http://known/',
65+ ... 'unknown_link': 'http://unknown/'})
66+ >>> bound_root = service_root.bind(representation)
67+
68+ >>> print bound_root.get_parameter("scalar_value").link
69+ None
70+
71+ >>> known_resource = bound_root.get_parameter("known_link")
72+ >>> unknown_resource = bound_root.get_parameter("unknown_link")
73+
74+ >>> print known_resource.link.can_follow
75+ True
76+ >>> print unknown_resource.link.can_follow
77+ False
78+
79+A link whose type is unknown is a link to a resource not described by
80+WADL. Following this link using .linked_resource or .link.follow will
81+cause a wadllib error. You'll need to follow the link using a general
82+HTTP library or some other tool.
83+
84+ >>> known_resource.link.follow
85+ <wadllib.application.Resource object ...>
86+ >>> known_resource.linked_resource
87+ <wadllib.application.Resource object ...>
88+
89+ >>> unknown_resource.link.follow
90+ Traceback (most recent call last):
91+ ...
92+ WADLError: Cannot follow a link when the target has no WADL
93+ description. Try using a general HTTP client instead.
94+
95+ >>> unknown_resource.linked_resource
96+ Traceback (most recent call last):
97+ ...
98+ WADLError: Cannot follow a link when the target has no WADL
99+ description. Try using a general HTTP client instead.
100+
101 Creating a Resource from a representation definition
102 ====================================================
103
104@@ -314,7 +363,6 @@
105 ... 'total_size', 'application/json').get_value()
106 63
107
108-======================
109 Resource instantiation
110 ======================
111
112@@ -351,7 +399,6 @@
113 you've already processed the representation, pass in False for the
114 'representation_needs_processing' argument.
115
116- >>> import simplejson
117 >>> processed_limi_data = simplejson.loads(unicode(limi_data))
118 >>> bound_limi = Resource(wadl, "http://api.launchpad.dev/beta/~limi",
119 ... "http://api.launchpad.dev/beta/#person", processed_limi_data,
120@@ -419,7 +466,7 @@
121 None
122
123 Data type conversion
124-====================
125+--------------------
126
127 The values of date and dateTime parameters are automatically converted to
128 Python datetime objects.
129@@ -464,7 +511,6 @@
130 >>> print bound_root.get_parameter('a_datetime').get_value()
131 None
132
133-=======================
134 Representation creation
135 =======================
136
137@@ -535,7 +581,6 @@
138 ...
139 ValueError: Unsupported media type: 'text/unknown'
140
141-=======
142 Options
143 =======
144
145@@ -565,7 +610,6 @@
146 'has_options': valid values are: "Value 1", "Value 2"
147
148
149-================
150 Error conditions
151 ================
152
153
154=== modified file 'src/wadllib/application.py'
155--- src/wadllib/application.py 2010-05-03 16:15:19 +0000
156+++ src/wadllib/application.py 2011-02-03 16:29:49 +0000
157@@ -388,6 +388,19 @@
158 return method
159 return None
160
161+ def parameters(self, media_type=None):
162+ """A list of this resource's parameters.
163+
164+ :param media_type: Media type of the representation definition
165+ whose parameters are being named. Must be present unless
166+ this resource is bound to a representation.
167+
168+ :raise NoBoundRepresentationError: If this resource is not
169+ bound to a representation and media_type was not provided.
170+ """
171+ return self._find_representation_definition(
172+ media_type).params(self)
173+
174 def parameter_names(self, media_type=None):
175 """A list naming this resource's parameters.
176
177@@ -914,8 +927,22 @@
178 for option_tag in self.tag.findall(wadl_xpath('option'))]
179
180 @property
181+ def link(self):
182+ """Get the link to another resource.
183+
184+ The link may be examined and, if its type is of a known WADL
185+ description, it may be followed.
186+
187+ :return: A Link object, or None.
188+ """
189+ link_tag = self.tag.find(wadl_xpath('link'))
190+ if link_tag is None:
191+ return None
192+ return Link(self, link_tag)
193+
194+ @property
195 def linked_resource(self):
196- """Find the type of resource linked to by this parameter.
197+ """Follow a link from this parameter to a new resource.
198
199 This only works for parameters whose WADL definition includes a
200 <link> tag that points to a known WADL description.
201@@ -923,10 +950,10 @@
202 :return: A Resource object for the resource at the other end
203 of the link.
204 """
205- link_tag = self.tag.find(wadl_xpath('link'))
206- if link_tag is None:
207+ link = self.link
208+ if link is None:
209 raise ValueError("This parameter isn't a link to anything.")
210- return Link(self, link_tag).resolve_definition()
211+ return link.follow
212
213 class Option(WADLBase):
214 """One of a set of possible values for a parameter."""
215@@ -949,7 +976,7 @@
216 """A link from one resource to another.
217
218 Calling resolve_definition() on a Link will give you a Resource for the
219- type of resource linked to.
220+ type of resource linked to. An alias for this is 'follow'.
221 """
222
223 def __init__(self, parameter, link_tag):
224@@ -962,6 +989,28 @@
225 self.parameter = parameter
226 self.tag = link_tag
227
228+ @property
229+ def follow(self):
230+ """Follow the link to another Resource."""
231+ if not self.can_follow:
232+ raise WADLError("Cannot follow a link when the target has no "
233+ "WADL description. Try using a general HTTP "
234+ "client instead.")
235+ return self.resolve_definition()
236+
237+ @property
238+ def can_follow(self):
239+ """Can this link be followed within wadllib?
240+
241+ wadllib can follow a link if it points to a resource that has
242+ a WADL definition.
243+ """
244+ try:
245+ definition_url = self._get_definition_url()
246+ except WADLError:
247+ return False
248+ return True
249+
250 def _definition_factory(self, id):
251 """Turn a resource type ID into a ResourceType."""
252 return Resource(
253
254=== modified file 'src/wadllib/version.txt'
255--- src/wadllib/version.txt 2011-02-03 15:46:06 +0000
256+++ src/wadllib/version.txt 2011-02-03 16:29:49 +0000
257@@ -1,1 +1,1 @@
258-1.1.8
259+1.2.0

Subscribers

People subscribed via source and target branches