Merge lp:~jtv/launchpad/branch-url into lp:launchpad

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Aaron Bentley
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~jtv/launchpad/branch-url
Merge into: lp:launchpad
Prerequisite: lp:~jtv/launchpad/bug-507681
Diff against target: 224 lines (+90/-15)
5 files modified
lib/lp/code/configure.zcml (+1/-0)
lib/lp/code/interfaces/branch.py (+12/-1)
lib/lp/code/model/branch.py (+28/-2)
lib/lp/code/tests/test_branch.py (+47/-0)
lib/lp/code/xmlrpc/branch.py (+2/-12)
To merge this branch: bzr merge lp:~jtv/launchpad/branch-url
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+19422@code.launchpad.net

Commit message

Compose public URLs for branches.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

= IBranch.composePublicURL =

It turns out to be useful to have a method to compose a public URL for a branch on a given protocol. For the Translations buildfarm work, as the driving use-case, it makes it easy for us to open up a single firewall hole for a slave to check out a read-only copy of a public branch.

Test:

{{{
./bin/test -vv -t TestComposePublicURL
}}}

There is a bunch of pre-existing lint in {{{lib/lp/code/interfaces/branch.py}}} that's obviously caused by a confused checker. It complains about named parameters, thinking the "=" signs are assignments. I left those untouched, but eliminated all other lint (including a drive-by on a duplicate import).

Jeroen

Revision history for this message
Aaron Bentley (abentley) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/configure.zcml'
2--- lib/lp/code/configure.zcml 2010-02-17 11:19:42 +0000
3+++ lib/lp/code/configure.zcml 2010-02-19 17:26:20 +0000
4@@ -395,6 +395,7 @@
5 branch_type
6 name
7 url
8+ composePublicURL
9 whiteboard
10 target
11 mirror_status_message
12
13=== modified file 'lib/lp/code/interfaces/branch.py'
14--- lib/lp/code/interfaces/branch.py 2010-02-17 22:34:52 +0000
15+++ lib/lp/code/interfaces/branch.py 2010-02-19 17:26:21 +0000
16@@ -1,4 +1,4 @@
17-# Copyright 2009 Canonical Ltd. This software is licensed under the
18+# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
19 # GNU Affero General Public License version 3 (see the file LICENSE).
20
21 # pylint: disable-msg=E0211,E0213,F0401,W0611
22@@ -356,6 +356,17 @@
23 "This is the external location where the Bazaar "
24 "branch is hosted.")))
25
26+ @operation_parameters(
27+ scheme=TextLine(title=_("URL scheme"), default=u'http'))
28+ @export_read_operation()
29+ def composePublicURL(scheme='http'):
30+ """Return a public URL for the branch using the given protocol.
31+
32+ :param scheme: a protocol name accepted by the public
33+ code-hosting API. (As a legacy issue, 'sftp' is also
34+ accepted).
35+ """
36+
37 description = exported(
38 Text(
39 title=_('Description'), required=False,
40
41=== modified file 'lib/lp/code/model/branch.py'
42--- lib/lp/code/model/branch.py 2010-02-02 22:26:04 +0000
43+++ lib/lp/code/model/branch.py 2010-02-19 17:26:21 +0000
44@@ -1,4 +1,4 @@
45-# Copyright 2009 Canonical Ltd. This software is licensed under the
46+# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
47 # GNU Affero General Public License version 3 (see the file LICENSE).
48
49 # pylint: disable-msg=E0611,W0212,W0141,F0401
50@@ -7,9 +7,11 @@
51 __all__ = [
52 'Branch',
53 'BranchSet',
54+ 'compose_public_url',
55 ]
56
57 from datetime import datetime
58+import os.path
59
60 from bzrlib.branch import Branch as BzrBranch
61 from bzrlib.revision import NULL_REVISION
62@@ -27,6 +29,8 @@
63 from sqlobject import (
64 ForeignKey, IntCol, StringCol, BoolCol, SQLMultipleJoin, SQLRelatedJoin)
65
66+from lazr.uri import URI
67+
68 from canonical.config import config
69 from canonical.database.constants import DEFAULT, UTC_NOW
70 from canonical.database.sqlbase import (
71@@ -75,7 +79,6 @@
72 from lp.registry.interfaces.person import (
73 validate_person_not_private_membership, validate_public_person)
74 from lp.services.job.interfaces.job import JobStatus
75-from lp.services.job.model.job import Job
76 from lp.services.mail.notificationrecipientset import (
77 NotificationRecipientSet)
78
79@@ -478,6 +481,14 @@
80 is_dev_focus = False
81 return bazaar_identity(self, is_dev_focus)
82
83+ def composePublicURL(self, scheme='http'):
84+ """See `IBranch`."""
85+ # Not all protocols work for private branches.
86+ public_schemes = ['http']
87+ assert not (self.private and scheme in public_schemes), (
88+ "Private branch %s has no public URL." % self.unique_name)
89+ return compose_public_url(scheme, self.unique_name)
90+
91 @property
92 def warehouse_url(self):
93 """See `IBranch`."""
94@@ -1287,3 +1298,18 @@
95 """
96 update_trigger_modified_fields(branch)
97 send_branch_modified_notifications(branch, event)
98+
99+
100+def compose_public_url(scheme, unique_name, suffix=None):
101+ # Avoid circular imports.
102+ from lp.code.xmlrpc.branch import PublicCodehostingAPI
103+
104+ # Accept sftp as a legacy protocol.
105+ accepted_schemes = set(PublicCodehostingAPI.supported_schemes)
106+ accepted_schemes.add('sftp')
107+ assert scheme in accepted_schemes, "Unknown scheme: %s" % scheme
108+ host = URI(config.codehosting.supermirror_root).host
109+ path = '/' + unique_name
110+ if suffix is not None:
111+ path = os.path.join(path, suffix)
112+ return str(URI(scheme=scheme, host=host, path=path))
113
114=== modified file 'lib/lp/code/tests/test_branch.py'
115--- lib/lp/code/tests/test_branch.py 2009-12-14 00:04:19 +0000
116+++ lib/lp/code/tests/test_branch.py 2010-02-19 17:26:20 +0000
117@@ -17,6 +17,7 @@
118 BranchSubscriptionDiffSize, BranchSubscriptionNotificationLevel,
119 CodeReviewNotificationLevel)
120 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
121+from lp.code.xmlrpc.branch import PublicCodehostingAPI
122 from lp.registry.interfaces.series import SeriesStatus
123 from lp.registry.interfaces.pocket import PackagePublishingPocket
124 from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
125@@ -348,5 +349,51 @@
126 self.assertCanEdit(registrant, branch)
127
128
129+class TestComposePublicURL(TestCaseWithFactory):
130+
131+ layer = DatabaseFunctionalLayer
132+
133+ def setUp(self):
134+ super(TestComposePublicURL, self).setUp('admin@canonical.com')
135+
136+ def test_composePublicURL_accepts_supported_schemes(self):
137+ # composePublicURL accepts all schemes that PublicCodehostingAPI
138+ # supports.
139+ branch = self.factory.makeAnyBranch()
140+
141+ url_pattern = '%%s://bazaar.launchpad.dev/~%s/%s/%s' % (
142+ branch.owner.name, branch.product.name, branch.name)
143+ for scheme in PublicCodehostingAPI.supported_schemes:
144+ public_url = branch.composePublicURL(scheme)
145+ self.assertEqual(url_pattern % scheme, public_url)
146+
147+ # sftp support is also grandfathered in.
148+ sftp_url = branch.composePublicURL('sftp')
149+ self.assertEqual(url_pattern % 'sftp', sftp_url)
150+
151+ def test_composePublicURL_default_http(self):
152+ # The default scheme for composePublicURL is http.
153+ branch = self.factory.makeAnyBranch()
154+ prefix = 'http://'
155+ public_url = branch.composePublicURL()
156+ self.assertEqual(prefix, public_url[:len(prefix)])
157+
158+ def test_composePublicURL_unknown_scheme(self):
159+ # Schemes that aren't known to be supported are not accepted.
160+ branch = self.factory.makeAnyBranch()
161+ self.assertRaises(AssertionError, branch.composePublicURL, 'irc')
162+
163+ def test_composePublicURL_http_private(self):
164+ # Private branches don't have public http URLs.
165+ branch = self.factory.makeAnyBranch(private=True)
166+ self.assertRaises(AssertionError, branch.composePublicURL, 'http')
167+
168+ def test_composePublicURL_no_https(self):
169+ # There's no https support. If there were, it should probably
170+ # not work for private branches.
171+ branch = self.factory.makeAnyBranch()
172+ self.assertRaises(AssertionError, branch.composePublicURL, 'https')
173+
174+
175 def test_suite():
176 return unittest.TestLoader().loadTestsFromName(__name__)
177
178=== modified file 'lib/lp/code/xmlrpc/branch.py'
179--- lib/lp/code/xmlrpc/branch.py 2009-07-17 00:26:05 +0000
180+++ lib/lp/code/xmlrpc/branch.py 2010-02-19 17:26:21 +0000
181@@ -11,13 +11,10 @@
182 'BranchSetAPI', 'IBranchSetAPI', 'IPublicCodehostingAPI',
183 'PublicCodehostingAPI']
184
185-import os
186
187 from zope.component import getUtility
188 from zope.interface import Interface, implements
189
190-from lazr.uri import URI
191-
192 from canonical.config import config
193 from lp.bugs.interfaces.bug import IBugSet
194 from canonical.launchpad.webapp.interfaces import ILaunchBag, NotFoundError
195@@ -185,10 +182,6 @@
196
197 supported_schemes = 'bzr+ssh', 'http'
198
199- def _getBazaarHost(self):
200- """Return the hostname for the codehosting server."""
201- return URI(config.codehosting.supermirror_root).host
202-
203 def _getResultDict(self, branch, suffix=None, supported_schemes=None):
204 """Return a result dict with a list of URLs for the given branch.
205
206@@ -207,16 +200,13 @@
207
208 def _getUniqueNameResultDict(self, unique_name, suffix=None,
209 supported_schemes=None):
210+ from lp.code.model.branch import compose_public_url
211 if supported_schemes is None:
212 supported_schemes = self.supported_schemes
213 result = dict(urls=[])
214- host = self._getBazaarHost()
215- path = '/' + unique_name
216- if suffix is not None:
217- path = os.path.join(path, suffix)
218 for scheme in supported_schemes:
219 result['urls'].append(
220- str(URI(host=host, scheme=scheme, path=path)))
221+ compose_public_url(scheme, unique_name, suffix))
222 return result
223
224 @return_fault