Merge lp:~leonardr/lazr.restful/version-descriptions into lp:lazr.restful

Proposed by Leonard Richardson
Status: Merged
Approved by: Gary Poster
Approved revision: 125
Merged at revision: not available
Proposed branch: lp:~leonardr/lazr.restful/version-descriptions
Merge into: lp:lazr.restful
Diff against target: 321 lines (+127/-28)
7 files modified
src/lazr/restful/NEWS.txt (+8/-0)
src/lazr/restful/example/base/root.py (+15/-8)
src/lazr/restful/example/base/tests/wadl.txt (+30/-14)
src/lazr/restful/example/multiversion/tests/wadl.txt (+20/-3)
src/lazr/restful/interfaces/_rest.py (+26/-1)
src/lazr/restful/tales.py (+15/-2)
src/lazr/restful/templates/wadl-root.pt (+13/-0)
To merge this branch: bzr merge lp:~leonardr/lazr.restful/version-descriptions
Reviewer Review Type Date Requested Status
Gary Poster Approve
Review via email: mp+21075@code.launchpad.net

Description of the change

This branch adds two new attributes to web service configuration, "service_description" and "version_descriptions". "service_description" is supposed to be a human-readable overview of the web service; version_descriptions is a map of version names to human-readable overviews of what makes that version different.

Both attributes are put into <doc> tags in the generated WADL. For my next trick, I'll pull them out of the WADL and incorporate them into the generated Launchpad API documentation.

To post a comment you must log in.
Revision history for this message
Gary Poster (gary) wrote :

merge-conditional

Thank you

Gary

[2:46pm] gary_poster: leonardr: "[x for x in contents['service_doc']]" -> "list(contents['service_doc']]" ?
[2:47pm] gary_poster: leonardr: lines 189 and 191 of diff
[2:47pm] leonardr: gary: sure
[2:47pm] gary_poster: cool
[2:48pm] gary_poster: leonardr: do you intend to keep pdb around? line 286
[2:48pm] leonardr: gary, no, that's in by mistake
[2:48pm] gary_poster: cool
[2:49pm] gary_poster: leonardr: otherwise, r=gary. Will mark as such.
[2:49pm] leonardr: gary: also the mssing = object() needs to go
[2:49pm] gary_poster: ah, ok. Should have investigated more carefully then.

review: Approve
126. By Leonard Richardson

Response to feedback.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/lazr/restful/NEWS.txt'
2--- src/lazr/restful/NEWS.txt 2010-03-03 13:08:12 +0000
3+++ src/lazr/restful/NEWS.txt 2010-03-10 20:48:29 +0000
4@@ -5,6 +5,14 @@
5 Development
6 ===========
7
8+There are two new attributes of the web service configuratino,
9+"service_description" and "version_descriptions". Both are optional,
10+but they're useful for giving your users an overview of your web
11+service and of the differences between versions.
12+
13+0.9.22 (2010-03-05)
14+===================
15+
16 Special note: this version will break backwards compatibility in your
17 web service unless you take a special step. See
18 "last_version_with_named_mutator_operations" below.
19
20=== modified file 'src/lazr/restful/example/base/root.py'
21--- src/lazr/restful/example/base/root.py 2010-02-25 17:07:16 +0000
22+++ src/lazr/restful/example/base/root.py 2010-03-10 20:48:29 +0000
23@@ -385,12 +385,19 @@
24
25 class WebServiceConfiguration(BaseWebServiceConfiguration):
26 directives.publication_class(WebServiceTestPublication)
27- code_revision='test.revision'
28- default_batch_size=5
29- hostname='cookbooks.dev'
30- match_batch_size=50
31- active_versions=['1.0', 'devel']
32- last_version_with_mutator_named_operations=None
33- use_https=False
34- view_permission='lazr.restful.example.base.View'
35+ code_revision = 'test.revision'
36+ default_batch_size = 5
37+ hostname = 'cookbooks.dev'
38+ match_batch_size = 50
39+ active_versions = ['1.0', 'devel']
40+ service_description = """<p>This is a web service.</p>
41+ <p>It's got resources!</p>"""
42+ version_descriptions = { 'devel' : """<p>The unstable development
43+ version.</p>
44+
45+ <p>Don't use this unless you like changing things.</p>"""
46+ }
47+ last_version_with_mutator_named_operations = None
48+ use_https = False
49+ view_permission = 'lazr.restful.example.base.View'
50
51
52=== modified file 'src/lazr/restful/example/base/tests/wadl.txt'
53--- src/lazr/restful/example/base/tests/wadl.txt 2010-02-15 14:56:36 +0000
54+++ src/lazr/restful/example/base/tests/wadl.txt 2010-03-10 20:48:29 +0000
55@@ -6,7 +6,6 @@
56 format. These documents are similar to the HTML documents that provide
57 human beings with links to click and forms to fill out.
58
59-===============
60 Entry resources
61 ===============
62
63@@ -103,7 +102,6 @@
64 document that says "The resource at this URL is of type [foo]," where
65 [foo] is a reference to another WADL document.
66
67-====================
68 Collection resources
69 ====================
70
71@@ -137,7 +135,6 @@
72 >>> resource.attrib['type']
73 'http://...#cookbooks'
74
75-===========================
76 Scoped collection resources
77 ===========================
78
79@@ -165,7 +162,6 @@
80 >>> resource.attrib['type']
81 'http://...#recipe-page-resource'
82
83-=====================
84 Hosted file resources
85 =====================
86
87@@ -224,7 +220,6 @@
88 >>> resource.attrib['type']
89 'http://...#HostedFile'
90
91-================
92 The service root
93 ================
94
95@@ -257,19 +252,41 @@
96 located here are the descriptions of the JSON representations those
97 resources serve.
98
99-======================================
100-Description of the service root itself
101-======================================
102-
103-Let's whittle down the complexity a little by taking a look at the
104-description of the service root resource itself.
105+Description of the full service
106+--------------------------------
107+
108+The first two tags in the WADL file are human-readable documentation
109+for the entire web service.
110+
111
112 >>> from lxml.etree import _Comment
113 >>> children = [child for child in tree
114 ... if not isinstance(child, _Comment)]
115- >>> resources, service_root_type, service_root_repr = children[:3]
116+
117+ >>> service_doc, version_doc = children[:2]
118+ >>> print service_doc.attrib['title']
119+ About this service
120+ >>> for p_tag in service_doc:
121+ ... print p_tag.text
122+ This is a web service.
123+ It's got resources!
124+
125+ >>> print version_doc.attrib['title']
126+ About version devel
127+ >>> for p_tag in version_doc:
128+ ... print p_tag.text
129+ The unstable development version.
130+ Don't use this unless you like changing things.
131+
132+Description of the service root itself
133+--------------------------------------
134+
135+Let's whittle down the complexity a little more by taking a look at
136+the description of the service root resource itself.
137+
138+ >>> resources, service_root_type, service_root_repr = children[2:5]
139 >>> # We'll deal with the rest of the children later.
140- >>> other_children = children[3:]
141+ >>> other_children = children[5:]
142
143 The service root is an instance of a special resource type that
144 responds only to GET.
145@@ -397,7 +414,6 @@
146 Aha! There's a resource of type "service-root" right there at the
147 service root! Who would have thought?
148
149-========================
150 The rest of the document
151 ========================
152
153
154=== modified file 'src/lazr/restful/example/multiversion/tests/wadl.txt'
155--- src/lazr/restful/example/multiversion/tests/wadl.txt 2010-02-11 16:36:47 +0000
156+++ src/lazr/restful/example/multiversion/tests/wadl.txt 2010-03-10 20:48:29 +0000
157@@ -24,9 +24,10 @@
158 ... api_version=version).body
159 ... tree = etree.fromstring(wadl)
160 ...
161- ... keys = ("base service_root service_root_json pair_collection "
162- ... "pair_entry pair_full_json pair_diff_jaon pair_page "
163- ... "pair_page_json hosted_file hosted_file_representation"
164+ ... keys = ("service_doc version_doc base service_root "
165+ ... "service_root_json pair_collection pair_entry"
166+ ... "pair_full_json pair_diff_jaon pair_page pair_page_json"
167+ ... "hosted_file hosted_file_representation"
168 ... ).split()
169 ...
170 ... tags = [child for child in tree if not isinstance(child, _Comment)]
171@@ -39,6 +40,9 @@
172 is not present at all.
173
174 >>> contents = wadl_contents_for_version('beta')
175+ >>> print contents['version_doc'].attrib['title']
176+ About version beta
177+
178 >>> print contents['base'].attrib['base']
179 http://multiversion.dev/beta/
180
181@@ -46,9 +50,20 @@
182 >>> sorted([method.attrib['id'] for method in pair_collection])
183 ['key_value_pairs-get']
184
185+As a side note, see that the service documentation and version
186+documentation tags are empty, because this service's configuration
187+doesn't specify that information:
188+
189+ >>> len(list(contents['service_doc']))
190+ 0
191+ >>> len(list(contents['version_doc']))
192+ 0
193+
194 In '2.0', the by_value method is called 'byValue'.
195
196 >>> contents = wadl_contents_for_version('2.0')
197+ >>> print contents['version_doc'].attrib['title']
198+ About version 2.0
199 >>> print contents['base'].attrib['base']
200 http://multiversion.dev/2.0/
201
202@@ -59,6 +74,8 @@
203 In '3.0', the method changes its name to 'by_value'.
204
205 >>> contents = wadl_contents_for_version('3.0')
206+ >>> print contents['version_doc'].attrib['title']
207+ About version 3.0
208 >>> print contents['base'].attrib['base']
209 http://multiversion.dev/3.0/
210
211
212=== modified file 'src/lazr/restful/interfaces/_rest.py'
213--- src/lazr/restful/interfaces/_rest.py 2010-02-24 18:02:29 +0000
214+++ src/lazr/restful/interfaces/_rest.py 2010-03-10 20:48:29 +0000
215@@ -52,7 +52,7 @@
216 'IWebServiceVersion',
217 ]
218
219-from zope.schema import Bool, Int, List, TextLine
220+from zope.schema import Bool, Dict, Int, List, Text, TextLine
221 from zope.interface import Attribute, Interface
222 # These two should really be imported from zope.interface, but
223 # the import fascist complains because they are not in __all__ there.
224@@ -394,6 +394,16 @@
225 These are miscellaneous strings that may differ in different web
226 services.
227 """
228+ service_description = TextLine(
229+ title=u"Service description",
230+ description=u"""A human-readable description of the web service.
231+
232+ The description may contain HTML, but if it does, it must be a
233+ valid XHTML fragment.
234+ """,
235+ default=u"",
236+ )
237+
238 view_permission = TextLine(
239 title=u"View permission", default=u"zope.View",
240 description=u"The permission to use when checking object visibility.")
241@@ -448,6 +458,21 @@
242
243 This list must contain at least one version name.""")
244
245+ version_descriptions = Dict(
246+ key_type = TextLine(),
247+ value_type = Text(),
248+ title = u"Human-readable descriptions of the web service versions.",
249+ description = u"""A dictionary mapping version names to
250+ human-readable descriptions. The descriptions should describe
251+ what distinguishes this version from other versions, and
252+ mention if/when the version will be removed.
253+
254+ The descriptions may contain HTML, but if they do, they must be
255+ valid XHTML fragments.
256+ """,
257+ default = {}
258+ )
259+
260 last_version_with_mutator_named_operations = TextLine(
261 default=None,
262 description=u"""In earlier versions of lazr.restful, mutator methods
263
264=== modified file 'src/lazr/restful/tales.py'
265--- src/lazr/restful/tales.py 2010-02-18 15:02:10 +0000
266+++ src/lazr/restful/tales.py 2010-03-10 20:48:29 +0000
267@@ -32,8 +32,8 @@
268 ICollection, ICollectionField, IEntry, IJSONRequestCache,
269 IReferenceChoice, IResourceDELETEOperation, IResourceGETOperation,
270 IResourceOperation, IResourcePOSTOperation, IScopedCollection,
271- ITopLevelEntryLink, IWebServiceClientRequest, IWebServiceVersion,
272- LAZR_WEBSERVICE_NAME)
273+ ITopLevelEntryLink, IWebServiceClientRequest, IWebServiceConfiguration,
274+ IWebServiceVersion, LAZR_WEBSERVICE_NAME)
275 from lazr.restful.utils import get_current_web_service_request
276
277
278@@ -221,6 +221,19 @@
279 return self._service_root_url()
280
281 @property
282+ def description(self):
283+ return getUtility(IWebServiceConfiguration).service_description
284+
285+ @property
286+ def service_version(self):
287+ return self.resource.request.version
288+
289+ @property
290+ def version_description(self):
291+ config = getUtility(IWebServiceConfiguration)
292+ return config.version_descriptions.get(self.service_version, None)
293+
294+ @property
295 def top_level_resources(self):
296 """Return a list of dicts describing the top-level resources."""
297 resource_dicts = []
298
299=== modified file 'src/lazr/restful/templates/wadl-root.pt'
300--- src/lazr/restful/templates/wadl-root.pt 2009-10-21 13:41:12 +0000
301+++ src/lazr/restful/templates/wadl-root.pt 2010-03-10 20:48:29 +0000
302@@ -11,6 +11,19 @@
303 xmlns:metal="http://xml.zope.org/namespaces/metal"
304 >
305
306+ <wadl:doc xmlns="http://www.w3.org/1999/xhtml"
307+ title="About this service"
308+ tal:content="structure context/wadl:description">
309+ Version-independent description of the web service.
310+ </wadl:doc>
311+
312+ <wadl:doc xmlns="http://www.w3.org/1999/xhtml"
313+ tal:define="version context/wadl:service_version"
314+ tal:attributes="title string:About version ${version}"
315+ tal:content="structure context/wadl:version_description">
316+ Description of this version of the web service.
317+ </wadl:doc>
318+
319 <!--There is one "service root" resource, located (as you'd expect)
320 at the service root. This very document is the WADL
321 representation of the "service root" resource.-->

Subscribers

People subscribed via source and target branches