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

Proposed by Ian Booth
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 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

Lint

15072. By Ian Booth

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