Merge lp:~leonardr/lazr.restful/bump-version into lp:lazr.restful
- bump-version
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~leonardr/lazr.restful/bump-version |
Merge into: | lp:lazr.restful |
Diff against target: |
193 lines (+113/-14) 6 files modified
src/lazr/restful/NEWS.txt (+9/-8) src/lazr/restful/_resource.py (+27/-3) src/lazr/restful/example/multiversion/tests/introduction.txt (+2/-1) src/lazr/restful/example/multiversion/tests/wadl.txt (+67/-0) src/lazr/restful/tales.py (+7/-1) src/lazr/restful/version.txt (+1/-1) |
To merge this branch: | bzr merge lp:~leonardr/lazr.restful/bump-version |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Nelson (community) | code | Approve | |
Review via email: mp+19107@code.launchpad.net |
Commit message
Description of the change
Leonard Richardson (leonardr) wrote : | # |
- 115. By Leonard Richardson
-
Added missing file.
Michael Nelson (michael.nelson) wrote : | # |
Thanks for all the changes Leonard. IRC log below for the record (and sorry Bjorn if you read this - I normally put the quoted diff, but I had to rush to finish this one on time!)
17:02 < noodles775> leonardr: on line 45 of the MP diff, is this really the earliest version, or the earliest *active* version? (which
17:03 < leonardr> noodles775: inactive versions don't exist in the lazr.restful installation, only in peoples' memories, so the
17:03 < leonardr> i can change the wording
17:04 < noodles775> leonardr: in which case, why can len(config.
17:04 < leonardr> noodles775: it's kind of confusing but i don't want to change the interface becuase that'll cause backward
17:04 < leonardr> there are two fields in the interface
17:04 < leonardr> active_versions and latest_
17:05 < leonardr> active_versions is a list, the other is a stirng
17:05 < leonardr> lazr.restful serves web services for all of those strings
17:05 -!- danilos [~danilo@
17:05 < leonardr> so if active_versions is =['beta', '1.0'] and latest_
17:05 < leonardr> if active_versions is [] and latest_
17:05 < leonardr> though i don't recommend doing that
17:06 < noodles775> So if active_versions is =['beta', '1.0'] and latest_
17:06 < noodles775> leonardr: ^^
17:07 < leonardr> noodles775: i'm having trouble parsing that sentence, but i can tell you that in that case earliest_version is 'beta'
17:07 < leonardr> latest_
17:07 < noodles775> OK, as the code implies, I'm just confused by the names.
17:08 < leonardr> noodles775: i'm kind of leaning towards treating the last item in active_versions as latest_
17:08 < leonardr> but that would be a separate job
17:09 -!- salgado-lunch is now known as salgado
17:09 < noodles775> Right, that would make sense. (do you mind adding an XXX?)
17:09 < leonardr> sure
17:11 < noodles775> leonardr: just a style question, is the comment on the empty line 69 intentional?
17:11 < leonardr> yeah, it's like a para break. i don't know if we allow that though
17:12 < noodles775> Yeah, I think a blank line (without the comment) serves just as well, but if the style guide doesn't say either
17:12 < leonardr> style guide doesn't mention it
17:13 < leonardr> if i see a blank line i think the first comment was talking about the blank line and the second comment is different
17:13 < noodles775> leonardr: s/marker_
17:13 < noodles775> sure
17:19 -!- danilos [~danilo@
Preview Diff
1 | === modified file 'src/lazr/restful/NEWS.txt' |
2 | --- src/lazr/restful/NEWS.txt 2010-01-12 17:41:25 +0000 |
3 | +++ src/lazr/restful/NEWS.txt 2010-02-11 15:31:14 +0000 |
4 | @@ -2,19 +2,20 @@ |
5 | NEWS for lazr.restful |
6 | ===================== |
7 | |
8 | -Development |
9 | -=========== |
10 | +0.9.18 (2010-02-11) |
11 | +=================== |
12 | |
13 | Special note: this version contains backwards-incompatible |
14 | changes. You *must* change your configuration object to get your code |
15 | -to work in this version! See "active_versions" below. |
16 | +to work in this version! See "active_versions" and |
17 | +"latest_version_uri_prefix" below. |
18 | |
19 | Added a versioning system for web services. Clients can now request |
20 | -the "trunk" of a web service as well as one published version. Apart |
21 | -from the URIs served, the two web services are exactly the same. There |
22 | -is no way to serve two different versions of a web service without |
23 | -defining both versions from scratch. (There will eventually be |
24 | -annotations to make this easy.) |
25 | +any number of distinct versions as well as a floating "trunk" which is |
26 | +always the most recent version. By using version-aware annotations, |
27 | +developers can publish the same data model differently over time. See |
28 | +the example web service in example/multiversion/ to see how the |
29 | +annotations work. |
30 | |
31 | This release introduces a new field to IWebServiceConfiguration: |
32 | latest_version_uri_prefix. If you are rolling your own |
33 | |
34 | === modified file 'src/lazr/restful/_resource.py' |
35 | --- src/lazr/restful/_resource.py 2010-02-03 21:22:19 +0000 |
36 | +++ src/lazr/restful/_resource.py 2010-02-11 15:31:14 +0000 |
37 | @@ -1598,6 +1598,14 @@ |
38 | collection_classes = [] |
39 | singular_names = {} |
40 | plural_names = {} |
41 | + |
42 | + # Determine the name of the earliest version. We'll be using this later. |
43 | + config = getUtility(IWebServiceConfiguration) |
44 | + if len(config.active_versions) > 0: |
45 | + earliest_version = config.active_versions[0] |
46 | + else: |
47 | + earliest_version = config.latest_version_uri_prefix |
48 | + |
49 | for registration in sorted(site_manager.registeredAdapters()): |
50 | provided = registration.provided |
51 | if IInterface.providedBy(provided): |
52 | @@ -1610,10 +1618,26 @@ |
53 | # of the classes with schemas, which we do describe. |
54 | |
55 | # Make sure we have a registration relevant to |
56 | - # this version. A given entry may be have one |
57 | + # this version. A given entry may have one |
58 | # registration for every web service version. |
59 | - schema, version = registration.required |
60 | - if not version.providedBy(self.request): |
61 | + schema, version_marker = registration.required |
62 | + |
63 | + if (version_marker is IWebServiceClientRequest |
64 | + and self.request.version != earliest_version): |
65 | + # We are generating WADL for some version |
66 | + # other than the earliest version, and this is |
67 | + # a registration for the earliest version. We |
68 | + # can ignore it. |
69 | + # |
70 | + # We need this special test because the normal |
71 | + # test (below) is useless when |
72 | + # marker_interface is |
73 | + # IWebServiceClientRequest. Since all request |
74 | + # objects provide IWebServiceClientRequest |
75 | + # directly, it will always show up in |
76 | + # providedBy(self.request). |
77 | + continue |
78 | + if not version_marker in providedBy(self.request): |
79 | continue |
80 | |
81 | # Make sure that no other entry class is using this |
82 | |
83 | === modified file 'src/lazr/restful/example/multiversion/tests/introduction.txt' |
84 | --- src/lazr/restful/example/multiversion/tests/introduction.txt 2010-02-08 18:59:13 +0000 |
85 | +++ src/lazr/restful/example/multiversion/tests/introduction.txt 2010-02-11 15:31:14 +0000 |
86 | @@ -232,7 +232,8 @@ |
87 | If an entry field is not published in a certain version, the |
88 | corresponding field resource does not exist for that version. |
89 | |
90 | - >>> webservice.get('/pairs/foo/deleted', api_version='beta') |
91 | + >>> print webservice.get('/pairs/foo/deleted', api_version='beta').body |
92 | + Object: ... |
93 | Traceback (most recent call last): |
94 | ... |
95 | NotFound: ... name: u'deleted' |
96 | |
97 | === added file 'src/lazr/restful/example/multiversion/tests/wadl.txt' |
98 | --- src/lazr/restful/example/multiversion/tests/wadl.txt 1970-01-01 00:00:00 +0000 |
99 | +++ src/lazr/restful/example/multiversion/tests/wadl.txt 2010-02-11 15:31:14 +0000 |
100 | @@ -0,0 +1,67 @@ |
101 | +Multi-version WADL documents |
102 | +**************************** |
103 | + |
104 | +A given version of the web service generates a WADL document which |
105 | +describes that version only. Let's go through the WADL documents for |
106 | +the different versions and see how they differ. |
107 | + |
108 | + >>> from lazr.restful.testing.webservice import WebServiceCaller |
109 | + >>> webservice = WebServiceCaller(domain='multiversion.dev') |
110 | + |
111 | +We'll start with a helper function that retrieves the WADL description |
112 | +for a given version of the key-value web service, and decomposes the |
113 | +top-level tags in the WADL document into a dictionary for easy access |
114 | +later. This works because all versions of the web service publish a |
115 | +single top-level collection and a single entry type, so the document's |
116 | +top-level structure is always the same. |
117 | + |
118 | + >>> from lxml import etree |
119 | + >>> from lxml.etree import _Comment |
120 | + >>> def wadl_contents_for_version(version): |
121 | + ... """Parse the key-value service's WADL into a dictionary.""" |
122 | + ... wadl = webservice.get( |
123 | + ... '/', media_type='application/vnd.sun.wadl+xml', |
124 | + ... api_version=version).body |
125 | + ... tree = etree.fromstring(wadl) |
126 | + ... |
127 | + ... keys = ("base service_root service_root_json pair_collection " |
128 | + ... "pair_entry pair_full_json pair_diff_jaon pair_page " |
129 | + ... "pair_page_json hosted_file hosted_file_representation" |
130 | + ... ).split() |
131 | + ... |
132 | + ... tags = [child for child in tree if not isinstance(child, _Comment)] |
133 | + ... contents = {} |
134 | + ... for i in range(0, len(keys)): |
135 | + ... contents[keys[i]] = tags[i] |
136 | + ... return contents |
137 | + |
138 | +Let's take a look at the differences. in 'beta', the 'by_value' method |
139 | +is not present at all. |
140 | + |
141 | + >>> contents = wadl_contents_for_version('beta') |
142 | + >>> print contents['base'].attrib['base'] |
143 | + http://multiversion.dev/beta/ |
144 | + |
145 | + >>> pair_collection = contents['pair_collection'] |
146 | + >>> sorted([method.attrib['id'] for method in pair_collection]) |
147 | + ['key_value_pairs-get'] |
148 | + |
149 | +In '2.0', the by_value method is called 'byValue'. |
150 | + |
151 | + >>> contents = wadl_contents_for_version('2.0') |
152 | + >>> print contents['base'].attrib['base'] |
153 | + http://multiversion.dev/2.0/ |
154 | + |
155 | + >>> pair_collection = contents['pair_collection'] |
156 | + >>> sorted([method.attrib['id'] for method in pair_collection]) |
157 | + ['key_value_pairs-byValue', 'key_value_pairs-get'] |
158 | + |
159 | +In '3.0', the method changes its name to 'byValue'. |
160 | + |
161 | + >>> contents = wadl_contents_for_version('3.0') |
162 | + >>> print contents['base'].attrib['base'] |
163 | + http://multiversion.dev/3.0/ |
164 | + |
165 | + >>> pair_collection = contents['pair_collection'] |
166 | + >>> sorted([method.attrib['id'] for method in pair_collection]) |
167 | + ['key_value_pairs-by_value', 'key_value_pairs-get'] |
168 | |
169 | === modified file 'src/lazr/restful/tales.py' |
170 | --- src/lazr/restful/tales.py 2010-01-11 18:27:43 +0000 |
171 | +++ src/lazr/restful/tales.py 2010-02-11 15:31:14 +0000 |
172 | @@ -291,7 +291,13 @@ |
173 | for interface in (IResourceGETOperation, IResourcePOSTOperation): |
174 | operations.extend(getGlobalSiteManager().adapters.lookupAll( |
175 | (model_class, request_interface), interface)) |
176 | - return [{'name' : name, 'op' : op} for name, op in operations] |
177 | + |
178 | + # An operation that was present in an earlier version but was |
179 | + # removed in the current version will show up in this list as |
180 | + # an stub function that returns None. Since we don't want that |
181 | + # operation to show up in this version, we'll filter it out. |
182 | + return [{'name' : name, 'op' : op} for name, op in operations |
183 | + if IResourceOperation.implementedBy(op)] |
184 | |
185 | |
186 | class WadlEntryInterfaceAdapterAPI(WadlResourceAdapterAPI): |
187 | |
188 | === modified file 'src/lazr/restful/version.txt' |
189 | --- src/lazr/restful/version.txt 2009-11-10 14:30:52 +0000 |
190 | +++ src/lazr/restful/version.txt 2010-02-11 15:31:14 +0000 |
191 | @@ -1,1 +1,1 @@ |
192 | -0.9.17 |
193 | +0.9.18 |
This branch adds multi-version tests for the WADL generation. When I tried to give multi-version features to lazr.restfulclient, it didn't work because the WADL was malformed. The two main problems were:
1. Unless you got the WADL for the latest version, lazr.restful saw multiple registrations for an entry adapter and crashed. This is because lazr.restful was looking for registrations that matched the marker interface (let's say) IWebServiceVers ion1_0. But IWebServiceVers ion2_0 and IWebServiceVers ion3_0 subclass IWebServiceVers ion1_0, so the entry adpater registrations for those versions were picked up as well.
2. The stub function that signals the removal of a named operation as of a particular version was being treated as a (malformed) named operation in its own right.
I fixed these problems in ways that should make sense when you look at the code.
This branch also fixes a test failure and prepares a new release of lazr.restful. (I thought that would be the only purpose of this branch, until lazr.restfulclient started failing on me.)