Merge lp:~wallyworld/launchpad/launchpadlib-services-974139 into lp:launchpad

Proposed by Ian Booth on 2012-04-10
Status: Work in progress
Proposed branch: lp:~wallyworld/launchpad/launchpadlib-services-974139
Merge into: lp:launchpad
Diff against target: 219 lines (+54/-56)
8 files modified
lib/lp/app/configure.zcml (+0/-8)
lib/lp/app/interfaces/services.py (+25/-1)
lib/lp/app/services.py (+13/-7)
lib/lp/app/tests/test_services.py (+9/-2)
lib/lp/registry/interfaces/webservice.py (+5/-1)
lib/lp/registry/services/tests/test_sharingservice.py (+1/-4)
lib/lp/services/webservice/services.py (+0/-32)
versions.cfg (+1/-1)
To merge this branch: bzr merge lp:~wallyworld/launchpad/launchpadlib-services-974139
Reviewer Review Type Date Requested Status
Launchpad code reviewers 2012-04-10 Pending
Review via email: mp+101349@code.launchpad.net

Commit Message

Rework export of ServiceFactory so that launchpadlib top level 'services' collection can be used.

Description of the Change

== Implementation ==

The changes in this branch allow launchpadlib to be used like so:

lp = Launchpad(...)
service = lp.services['myservice']
service.some_method()

Essentially, ServiceFactory is made a web service collection (requiring a collection_default_content to be defined). The default content method returns all registered services.

The changes mean that lp.services.webservice.services.ServicesLink needed to be removed.

This branch can be landed after lp:~wallyworld/launchpadlib/services-serviceroot

== Tests ==

The TestLaunchpadlib test case was changed to locate the service using the new syntax.
An additional test_registeredServices test was added for the new collection_default_content method.

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  versions.cfg
  lib/lp/app/configure.zcml
  lib/lp/app/services.py
  lib/lp/app/interfaces/services.py
  lib/lp/app/tests/test_services.py
  lib/lp/registry/interfaces/webservice.py
  lib/lp/registry/services/tests/test_sharingservice.py

To post a comment you must log in.

Unmerged revisions

15073. By Ian Booth on 2012-04-10

Lint

15072. By Ian Booth on 2012-04-10

Rework export of ServiceFactory so that launchpadlib top level 'services' collection can be used

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/configure.zcml'
2--- lib/lp/app/configure.zcml 2012-02-24 05:14:46 +0000
3+++ lib/lp/app/configure.zcml 2012-04-10 10:20:44 +0000
4@@ -74,13 +74,5 @@
5 <allow
6 interface="zope.publisher.interfaces.IPublishTraverse"/>
7 </securedutility>
8- <securedutility
9- class="lp.services.webservice.services.ServicesLink"
10- provides="lp.services.webservice.services.IServicesLink">
11- <allow
12- interface="lazr.restful.interfaces.ITopLevelEntryLink"/>
13- <allow
14- interface="lp.services.webapp.interfaces.ICanonicalUrlData"/>
15- </securedutility>
16
17 </configure>
18
19=== modified file 'lib/lp/app/interfaces/services.py'
20--- lib/lp/app/interfaces/services.py 2012-02-28 04:24:19 +0000
21+++ lib/lp/app/interfaces/services.py 2012-04-10 10:20:44 +0000
22@@ -12,8 +12,15 @@
23 ]
24
25 from lazr.restful.declarations import (
26+ collection_default_content,
27+ export_as_webservice_collection,
28 export_as_webservice_entry,
29+ export_read_operation,
30 exported,
31+ operation_for_version,
32+ operation_parameters,
33+ operation_returns_collection_of,
34+ operation_returns_entry,
35 )
36 from zope.interface import Interface
37 from zope.schema import TextLine
38@@ -24,6 +31,9 @@
39 class IService(Interface):
40 """Base interface for services."""
41
42+ export_as_webservice_entry(
43+ 'service', publish_web_link=False, as_of='beta')
44+
45 name = exported(
46 TextLine(
47 title=_('Name'),
48@@ -34,4 +44,18 @@
49 class IServiceFactory(Interface):
50 """Interface representing a factory used to access named services."""
51
52- export_as_webservice_entry(publish_web_link=False, as_of='beta')
53+ export_as_webservice_collection(IService)
54+
55+ @collection_default_content()
56+ @operation_returns_collection_of(IService)
57+ @export_read_operation()
58+ @operation_for_version("beta")
59+ def registeredServices():
60+ """Return all the registered services."""
61+
62+ @operation_parameters(name=TextLine(required=True))
63+ @operation_returns_entry(IService)
64+ @export_read_operation()
65+ @operation_for_version("beta")
66+ def getByName(name):
67+ """Lookup a service by name."""
68
69=== modified file 'lib/lp/app/services.py'
70--- lib/lp/app/services.py 2012-02-23 10:13:48 +0000
71+++ lib/lp/app/services.py 2012-04-10 10:20:44 +0000
72@@ -8,7 +8,10 @@
73 'ServiceFactory',
74 ]
75
76-from zope.component import getUtility
77+from zope.component import (
78+ getAllUtilitiesRegisteredFor,
79+ getUtility,
80+ )
81 from zope.interface import implements
82
83 from lp.app.interfaces.services import (
84@@ -19,9 +22,9 @@
85
86
87 class ServiceFactory(Navigation):
88- """Creates a named service.
89+ """Provides access to named services.
90
91- Services are traversed via urls of the form /services/<name>
92+ Services are traversed via urls of the form /+services/<name>
93 Implementation classes are registered as named zope utilities.
94 """
95
96@@ -31,7 +34,10 @@
97 super(ServiceFactory, self).__init__(None)
98
99 def traverse(self, name):
100- return self.getService(name)
101-
102- def getService(self, service_name):
103- return getUtility(IService, service_name)
104+ return self.getByName(name)
105+
106+ def getByName(self, name):
107+ return getUtility(IService, name)
108+
109+ def registeredServices(self):
110+ return getAllUtilitiesRegisteredFor(IService)
111
112=== modified file 'lib/lp/app/tests/test_services.py'
113--- lib/lp/app/tests/test_services.py 2012-02-28 04:24:19 +0000
114+++ lib/lp/app/tests/test_services.py 2012-04-10 10:20:44 +0000
115@@ -43,5 +43,12 @@
116 self.registerUtility(fake_service, IService, "fake")
117 context, view, request = test_traverse(
118 'https://launchpad.dev/api/devel/+services/fake')
119- self.assertEqual(getUtility(IServiceFactory), context)
120- self.assertEqual(fake_service, view)
121+ self.assertEqual(getUtility(IService, "fake"), context)
122+
123+ def test_registeredServices(self):
124+ # Test the ServiceFactory registeredServices method.
125+ login(ANONYMOUS)
126+ service_factory = getUtility(IServiceFactory)
127+ self.assertEqual(
128+ [service_factory.getByName('sharing')],
129+ service_factory.registeredServices())
130
131=== modified file 'lib/lp/registry/interfaces/webservice.py'
132--- lib/lp/registry/interfaces/webservice.py 2012-03-22 23:21:24 +0000
133+++ lib/lp/registry/interfaces/webservice.py 2012-04-10 10:20:44 +0000
134@@ -29,6 +29,7 @@
135 'IProductSet',
136 'IProjectGroup',
137 'IProjectGroupSet',
138+ 'IService',
139 'IServiceFactory',
140 'ISharingService',
141 'ISSHKey',
142@@ -43,7 +44,10 @@
143 # XXX: JonathanLange 2010-11-09 bug=673083: Legacy work-around for circular
144 # import bugs. Break this up into a per-package thing.
145 from lp import _schema_circular_imports
146-from lp.app.interfaces.services import IServiceFactory
147+from lp.app.interfaces.services import (
148+ IService,
149+ IServiceFactory,
150+ )
151 from lp.registry.interfaces.commercialsubscription import (
152 ICommercialSubscription,
153 )
154
155=== modified file 'lib/lp/registry/services/tests/test_sharingservice.py'
156--- lib/lp/registry/services/tests/test_sharingservice.py 2012-04-06 17:28:25 +0000
157+++ lib/lp/registry/services/tests/test_sharingservice.py 2012-04-10 10:20:44 +0000
158@@ -758,10 +758,7 @@
159 def setUp(self):
160 super(TestLaunchpadlib, self).setUp()
161 self.launchpad = self.factory.makeLaunchpadService(person=self.owner)
162- # XXX 2012-02-23 wallyworld bug 681767
163- # Launchpadlib can't do relative url's
164- self.service = self.launchpad.load(
165- '%s/+services/sharing' % self.launchpad._root_uri)
166+ self.service = self.launchpad.services['sharing']
167 flag = FeatureFixture(WRITE_FLAG)
168 flag.setUp()
169 self.addCleanup(flag.cleanUp)
170
171=== removed file 'lib/lp/services/webservice/services.py'
172--- lib/lp/services/webservice/services.py 2012-02-23 10:13:48 +0000
173+++ lib/lp/services/webservice/services.py 1970-01-01 00:00:00 +0000
174@@ -1,32 +0,0 @@
175-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
176-# GNU Affero General Public License version 3 (see the file LICENSE).
177-
178-"""A class for the top-level link to the services factory."""
179-
180-__metaclass__ = type
181-__all__ = [
182- 'IServicesLink',
183- 'ServicesLink',
184- ]
185-
186-from lazr.restful.interfaces import ITopLevelEntryLink
187-from zope.interface import implements
188-
189-from lp.app.interfaces.services import IServiceFactory
190-from lp.services.webapp.interfaces import ICanonicalUrlData
191-
192-
193-class IServicesLink(ITopLevelEntryLink, ICanonicalUrlData):
194- """A marker interface."""
195-
196-
197-class ServicesLink:
198- """The top-level link to the services factory."""
199- implements(IServicesLink)
200-
201- link_name = 'services'
202- entry_type = IServiceFactory
203-
204- inside = None
205- path = 'services'
206- rootsite = 'api'
207
208=== modified file 'versions.cfg'
209--- versions.cfg 2012-04-06 17:28:25 +0000
210+++ versions.cfg 2012-04-10 10:20:44 +0000
211@@ -35,7 +35,7 @@
212 Jinja2 = 2.2
213 keyring = 0.6.2
214 kombu = 2.1.1
215-launchpadlib = 1.9.12
216+launchpadlib = 1.9.13
217 lazr.amqp = 0.1
218 lazr.authentication = 0.1.1
219 lazr.batchnavigator = 1.2.10