Merge lp:~leonardr/lazr.restful/root-resource-url into lp:lazr.restful
- root-resource-url
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Gary Poster |
Approved revision: | 72 |
Merged at revision: | not available |
Proposed branch: | lp:~leonardr/lazr.restful/root-resource-url |
Merge into: | lp:lazr.restful |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~leonardr/lazr.restful/root-resource-url |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gary Poster | Approve | ||
Review via email: mp+10453@code.launchpad.net |
Commit message
Description of the change
Leonard Richardson (leonardr) wrote : | # |
- 69. By Leonard Richardson
-
Added a doctest for RootResourceAbs
oluteURL, and added two missing fields to IWebServiceConf iguration.
Leonard Richardson (leonardr) wrote : | # |
I did another revision that adds a doctest of RootResourceAbs
- 70. By Leonard Richardson
-
Replaced line removed by accident.
- 71. By Leonard Richardson
-
Removed unnecessary imports.
- 72. By Leonard Richardson
-
Removed abortive attempt at testing service_
root_uri_ prefix within the example web service.
Gary Poster (gary) wrote : | # |
(merge-conditional)
Hi Leonard. This is a great branch! The cleanup is very nice, and I can see how it would make the ISD webservice cleaner.
As we discussed on IRC, I'd like the configuration interface to clarify when it is necessary to fill out the new fields. As you said on IRC, for instance, it should not be necessary to fill them out for Launchpad. Maybe in the future we should divide these parts of the configuration interface out.
Other than that, +1.
Thanks,
Gary
Preview Diff
1 | === modified file 'src/lazr/restful/example/configuration.py' |
2 | --- src/lazr/restful/example/configuration.py 2009-08-11 18:36:20 +0000 |
3 | +++ src/lazr/restful/example/configuration.py 2009-08-19 21:06:41 +0000 |
4 | @@ -19,7 +19,10 @@ |
5 | """A configuration object for the cookbook web service.""" |
6 | implements(IWebServiceConfiguration) |
7 | |
8 | + hostname = "cookbooks.dev" |
9 | + port = None |
10 | path_override = "api" |
11 | + service_root_uri_prefix = "" |
12 | service_version_uri_prefix = "1.0" |
13 | view_permission = 'lazr.restful.example.View' |
14 | use_https = False |
15 | |
16 | === modified file 'src/lazr/restful/example/configure.zcml' |
17 | --- src/lazr/restful/example/configure.zcml 2009-08-04 18:23:18 +0000 |
18 | +++ src/lazr/restful/example/configure.zcml 2009-08-19 21:06:41 +0000 |
19 | @@ -99,12 +99,7 @@ |
20 | name="absolute_url" |
21 | /> |
22 | |
23 | - <adapter factory="lazr.restful.example.root.CookbookServiceRootAbsoluteURL" /> |
24 | - |
25 | - <adapter |
26 | - factory="lazr.restful.example.root.CookbookServiceRootAbsoluteURL" |
27 | - name="absolute_url" |
28 | - /> |
29 | + <adapter factory="lazr.restful.simple.RootResourceAbsoluteURL" /> |
30 | |
31 | <subscriber |
32 | for="lazr.restful.example.interfaces.ICookbook |
33 | |
34 | === modified file 'src/lazr/restful/example/root.py' |
35 | --- src/lazr/restful/example/root.py 2009-05-07 12:31:18 +0000 |
36 | +++ src/lazr/restful/example/root.py 2009-08-19 21:06:41 +0000 |
37 | @@ -359,25 +359,3 @@ |
38 | """Traverse to a top-level object.""" |
39 | obj = self.top_level_names.get(name) |
40 | return obj |
41 | - |
42 | - |
43 | -class CookbookServiceRootAbsoluteURL: |
44 | - """A basic implementation of IAbsoluteURL for the root object.""" |
45 | - implements(IAbsoluteURL) |
46 | - adapts(CookbookServiceRootResource, IDefaultBrowserLayer) |
47 | - |
48 | - HOSTNAME = "http://cookbooks.dev/" |
49 | - |
50 | - def __init__(self, context, request): |
51 | - """Initialize with respect to a context and request.""" |
52 | - self.version = getUtility( |
53 | - IWebServiceConfiguration).service_version_uri_prefix |
54 | - |
55 | - def __str__(self): |
56 | - """Return the semi-hard-coded base URL for the service.""" |
57 | - return self.HOSTNAME + self.version |
58 | - |
59 | - def __call__(self): |
60 | - """Return the semi-hard-coded URL to the service root resource.""" |
61 | - return str(self) + '/' |
62 | - |
63 | |
64 | === modified file 'src/lazr/restful/example/wsgi/configuration.py' |
65 | --- src/lazr/restful/example/wsgi/configuration.py 2009-08-03 13:12:20 +0000 |
66 | +++ src/lazr/restful/example/wsgi/configuration.py 2009-08-19 19:38:03 +0000 |
67 | @@ -18,6 +18,7 @@ |
68 | path_override = None |
69 | use_https = False |
70 | show_tracebacks = True |
71 | + service_root_uri_prefix = '' |
72 | service_version_uri_prefix = '1.0' |
73 | code_revision = '1' |
74 | default_batch_size = 50 |
75 | |
76 | === modified file 'src/lazr/restful/example/wsgi/configure.zcml' |
77 | --- src/lazr/restful/example/wsgi/configure.zcml 2009-08-11 15:26:14 +0000 |
78 | +++ src/lazr/restful/example/wsgi/configure.zcml 2009-08-19 19:38:03 +0000 |
79 | @@ -14,8 +14,8 @@ |
80 | factory="zope.traversing.browser.absoluteurl.AbsoluteURL" |
81 | /> |
82 | |
83 | - <!--This service's root object will generate its own URLs.--> |
84 | - <adapter factory="lazr.restful.example.wsgi.root.WSGIExampleWebServiceRootAbsoluteURL" /> |
85 | + <!--This is how the URL to the root resource will be generated.--> |
86 | + <adapter factory="lazr.restful.simple.RootResourceAbsoluteURL" /> |
87 | |
88 | <!--Register the service root as the implementation of lazr.restful's IServiceRootResource interface.--> |
89 | <utility |
90 | |
91 | === modified file 'src/lazr/restful/example/wsgi/root.py' |
92 | --- src/lazr/restful/example/wsgi/root.py 2009-08-17 16:22:22 +0000 |
93 | +++ src/lazr/restful/example/wsgi/root.py 2009-08-19 19:38:03 +0000 |
94 | @@ -7,15 +7,9 @@ |
95 | ] |
96 | |
97 | |
98 | -from zope.component import adapts, getUtility |
99 | -from zope.interface import implements |
100 | -from zope.traversing.browser.interfaces import IAbsoluteURL |
101 | - |
102 | from lazr.restful.example.wsgi.resources import ( |
103 | IKeyValuePair, PairSet, KeyValuePair) |
104 | -from lazr.restful.interfaces import IWebServiceConfiguration |
105 | from lazr.restful.simple import RootResource |
106 | -from lazr.restful.publisher import WebServiceRequestTraversal |
107 | |
108 | |
109 | class WSGIExampleWebServiceRootResource(RootResource): |
110 | @@ -30,28 +24,3 @@ |
111 | collections = dict(pairs=(IKeyValuePair, pairset)) |
112 | return collections, {} |
113 | |
114 | - |
115 | -class WSGIExampleWebServiceRootAbsoluteURL: |
116 | - """A basic implementation of `IAbsoluteURL` for the root object.""" |
117 | - |
118 | - implements(IAbsoluteURL) |
119 | - adapts(WSGIExampleWebServiceRootResource, WebServiceRequestTraversal) |
120 | - |
121 | - def __init__(self, context, request): |
122 | - """Initialize with respect to a context and request.""" |
123 | - config = getUtility(IWebServiceConfiguration) |
124 | - self.version = config.service_version_uri_prefix |
125 | - if config.use_https: |
126 | - self.schema = 'https' |
127 | - else: |
128 | - self.schema = 'http' |
129 | - self.hostname = config.hostname |
130 | - self.port = config.port |
131 | - |
132 | - def __str__(self): |
133 | - """Return the semi-hard-coded URL to the service root.""" |
134 | - if self.port is not None: |
135 | - return "%s://%s:%s/%s" % (self.schema, self.hostname, self.port, |
136 | - self.version) |
137 | - return "%s://%s/%s" % (self.schema, self.hostname, self.version) |
138 | - __call__ = __str__ |
139 | |
140 | === modified file 'src/lazr/restful/interfaces/_rest.py' |
141 | --- src/lazr/restful/interfaces/_rest.py 2009-07-07 19:42:37 +0000 |
142 | +++ src/lazr/restful/interfaces/_rest.py 2009-08-19 19:38:03 +0000 |
143 | @@ -397,10 +397,22 @@ |
144 | description=u"Whether or not requests to the web service are secured " |
145 | "through SSL.") |
146 | |
147 | + service_root_uri_prefix = TextLine( |
148 | + title=u"Any URL prefix necessary for the service root.", |
149 | + default=u"", |
150 | + description=u"If your web service is not located at the root of " |
151 | + "its domain (for instance, it's rooted at " |
152 | + "http://foo.com/web-service/ instead of http://api.foo/com/, " |
153 | + "put the URL prefix here. (In the example case, the URL prefix " |
154 | + "is 'web-service/').") |
155 | + |
156 | service_version_uri_prefix = Attribute( |
157 | """The versioning string, if any, to use as the URI prefix for web |
158 | service URIs. A popular string is 'beta', but you could also use |
159 | - a version number or the date the API was finalized.""") |
160 | + a version number or the date the API was finalized. |
161 | + |
162 | + The service version URI prefix shows up in URIs *after* any |
163 | + value for service_root_uri_prefix.""") |
164 | |
165 | code_revision = Attribute( |
166 | """A string designating the current revision number of the code |
167 | |
168 | === modified file 'src/lazr/restful/simple.py' |
169 | --- src/lazr/restful/simple.py 2009-08-18 19:08:30 +0000 |
170 | +++ src/lazr/restful/simple.py 2009-08-19 21:06:41 +0000 |
171 | @@ -14,7 +14,8 @@ |
172 | import traceback |
173 | import urllib |
174 | |
175 | -from zope.component import adapts, getMultiAdapter, queryMultiAdapter |
176 | +from zope.component import ( |
177 | + adapts, getMultiAdapter, getUtility, queryMultiAdapter) |
178 | from zope.interface import Attribute, Interface, implements |
179 | from zope.publisher.browser import BrowserRequest |
180 | from zope.publisher.interfaces import IPublication, IPublishTraverse, NotFound |
181 | @@ -24,10 +25,12 @@ |
182 | from zope.traversing.browser.absoluteurl import _insufficientContext, _safe |
183 | |
184 | from lazr.restful import EntryAdapterUtility, ServiceRootResource |
185 | -from lazr.restful.interfaces import ITraverseWithGet, IWebServiceLayer |
186 | +from lazr.restful.interfaces import ( |
187 | + ITraverseWithGet, IWebServiceConfiguration, IWebServiceLayer) |
188 | from lazr.restful.publisher import ( |
189 | WebServicePublicationMixin, WebServiceRequestTraversal) |
190 | |
191 | + |
192 | class PublicationMixin(object): |
193 | """A very simple implementation of `IPublication`. |
194 | |
195 | @@ -230,6 +233,50 @@ |
196 | return ({}, {}) |
197 | |
198 | |
199 | +class RootResourceAbsoluteURL: |
200 | + """A basic implementation of `IAbsoluteURL` for a root resource.""" |
201 | + |
202 | + implements(IAbsoluteURL) |
203 | + adapts(ServiceRootResource, WebServiceRequestTraversal) |
204 | + |
205 | + def __init__(self, context, request): |
206 | + """Initialize with respect to a context and request.""" |
207 | + config = getUtility(IWebServiceConfiguration) |
208 | + self.version = config.service_version_uri_prefix |
209 | + if config.use_https: |
210 | + self.schema = 'https' |
211 | + else: |
212 | + self.schema = 'http' |
213 | + self.hostname = config.hostname |
214 | + self.port = config.port |
215 | + self.prefix = config.service_root_uri_prefix |
216 | + |
217 | + def __str__(self): |
218 | + """Return the part of the URL that contains the hostname. |
219 | + |
220 | + This is called when constructing the URL for some resource. |
221 | + This string should not end with a slash; Zope's AbsoluteURL |
222 | + will provide the slash when joining this string with the |
223 | + __name__ of the subordinate resource. |
224 | + """ |
225 | + if self.port is not None: |
226 | + return "%s://%s:%s/%s%s" % ( |
227 | + self.schema, self.hostname, self.port, self.prefix, |
228 | + self.version) |
229 | + return "%s://%s/%s%s" % ( |
230 | + self.schema, self.hostname, self.prefix, self.version) |
231 | + |
232 | + def __call__(self): |
233 | + """Return the URL to the service root resource. |
234 | + |
235 | + This value is called when finding the URL to the service root |
236 | + resource, and it needs to end with a slash so that clients |
237 | + will be able to do relative URL resolution correctly with |
238 | + respect to the service root. |
239 | + """ |
240 | + return str(self) + '/' |
241 | + |
242 | + |
243 | class IMultiplePathPartLocation(Interface): |
244 | |
245 | """An ILocation-like interface for objects with multiple path parts. |
246 | @@ -284,4 +331,3 @@ |
247 | escaped_parts = [urllib.quote(part.encode('utf-8'), _safe) |
248 | for part in parts] |
249 | return start_url + "/" + "/".join(escaped_parts) |
250 | - __call__ = __str__ |
The IWebServiceConf iguration object contains (almost) all information necessary to generate the root URL of a web service. I've written three implementations of the same code to create three different root resource IAbsoluteURL implementations. One for the example web service, one for the example WSGI service, and one for a Canonical internal web service.
This branch moves this basic implementation into lazr.restful. simple, and changes the example web service and example WSGI web service to use the .simple implementation instead of custom implementations.
I added one field to IWebServiceConf iguration: service_ root_uri_ prefix. This is used in cases when the URI root of the web service is not the top of the hostname. For instance, if the web service is rooted at foo.com/webservice/ instead of webservice. foo.com/ .