Merge lp:~leonardr/lazr.restful/root-resource-url into lp:lazr.restful

Proposed by Leonard Richardson
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
Reviewer Review Type Date Requested Status
Gary Poster Approve
Review via email: mp+10453@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Leonard Richardson (leonardr) wrote :

The IWebServiceConfiguration 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 IWebServiceConfiguration: 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/.

69. By Leonard Richardson

Added a doctest for RootResourceAbsoluteURL, and added two missing fields to IWebServiceConfiguration.

Revision history for this message
Leonard Richardson (leonardr) wrote :

I did another revision that adds a doctest of RootResourceAbsoluteURL and adds 'hostname' and 'port' to IWebServiceConfiguration -- two fields that a lot of code was assuming were present, but which were not defined in the interface.

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.

Revision history for this message
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

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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__

Subscribers

People subscribed via source and target branches