Merge ~cjwatson/launchpad:branch-repository-api-redirects into launchpad:master
- Git
- lp:~cjwatson/launchpad
- branch-repository-api-redirects
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 4337fd11ef269da02dc2274ba8158b8f24ed112d |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:branch-repository-api-redirects |
Merge into: | launchpad:master |
Diff against target: |
324 lines (+115/-22) 5 files modified
lib/lp/app/browser/launchpad.py (+4/-4) lib/lp/app/browser/tests/test_launchpad.py (+22/-5) lib/lp/code/browser/tests/test_branchtraversal.py (+23/-9) lib/lp/registry/browser/person.py (+8/-4) lib/lp/registry/browser/tests/test_person.py (+58/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ioana Lasc (community) | Approve | ||
Review via email: mp+392544@code.launchpad.net |
Commit message
Send proper webservice redirects for branches and repositories
Description of the change
launchpadlib handles this correctly nowadays, but only if we send redirects to the correct target host (e.g. api.launchpad.net rather than code.launchpad.
To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/app/browser/launchpad.py b/lib/lp/app/browser/launchpad.py |
2 | index d31df20..eb4b9bb 100644 |
3 | --- a/lib/lp/app/browser/launchpad.py |
4 | +++ b/lib/lp/app/browser/launchpad.py |
5 | @@ -1,4 +1,4 @@ |
6 | -# Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
7 | +# Copyright 2009-2020 Canonical Ltd. This software is licensed under the |
8 | # GNU Affero General Public License version 3 (see the file LICENSE). |
9 | |
10 | """Browser code for the launchpad application.""" |
11 | @@ -739,7 +739,7 @@ class LaunchpadRootNavigation(Navigation): |
12 | path = '/'.join(self.request.stepstogo) |
13 | try: |
14 | branch, trailing = getUtility(IBranchLookup).getByLPPath(path) |
15 | - target_url = canonical_url(branch) |
16 | + target_url = canonical_url(branch, request=self.request) |
17 | if trailing != '': |
18 | target_url = urlappend(target_url, trailing) |
19 | except (NoLinkedBranch) as e: |
20 | @@ -789,7 +789,7 @@ class LaunchpadRootNavigation(Navigation): |
21 | try: |
22 | repository, trailing = getUtility(IGitLookup).getByPath(path) |
23 | if repository is not None: |
24 | - target_url = canonical_url(repository) |
25 | + target_url = canonical_url(repository, request=self.request) |
26 | if trailing: |
27 | target_url = urlappend(target_url, trailing) |
28 | except (InvalidNamespace, InvalidProductName, NameLookupFailed, |
29 | @@ -802,7 +802,7 @@ class LaunchpadRootNavigation(Navigation): |
30 | # Attempt a bzr lookup as well: |
31 | try: |
32 | branch, trailing = getUtility(IBranchLookup).getByLPPath(path) |
33 | - bzr_url = canonical_url(branch) |
34 | + bzr_url = canonical_url(branch, request=self.request) |
35 | if trailing != '': |
36 | bzr_url = urlappend(bzr_url, trailing) |
37 | |
38 | diff --git a/lib/lp/app/browser/tests/test_launchpad.py b/lib/lp/app/browser/tests/test_launchpad.py |
39 | index a9c6adf..b170634 100644 |
40 | --- a/lib/lp/app/browser/tests/test_launchpad.py |
41 | +++ b/lib/lp/app/browser/tests/test_launchpad.py |
42 | @@ -36,7 +36,10 @@ from lp.services.webapp.interfaces import ( |
43 | BrowserNotificationLevel, |
44 | ILaunchpadRoot, |
45 | ) |
46 | -from lp.services.webapp.servers import LaunchpadTestRequest |
47 | +from lp.services.webapp.servers import ( |
48 | + LaunchpadTestRequest, |
49 | + WebServiceTestRequest, |
50 | + ) |
51 | from lp.services.webapp.url import urlappend |
52 | from lp.testing import ( |
53 | admin_logged_in, |
54 | @@ -108,11 +111,12 @@ class TraversalMixin: |
55 | NotFound, self.traverse, path, |
56 | use_default_referer=use_default_referer) |
57 | |
58 | - def assertRedirects(self, segments, url): |
59 | - redirection = self.traverse(segments) |
60 | + def assertRedirects(self, segments, url, webservice=False): |
61 | + redirection = self.traverse(segments, webservice=webservice) |
62 | self.assertEqual(url, redirection.target) |
63 | |
64 | - def traverse(self, path, first_segment, use_default_referer=True): |
65 | + def traverse(self, path, first_segment, use_default_referer=True, |
66 | + webservice=False): |
67 | """Traverse to 'path' using a 'LaunchpadRootNavigation' object. |
68 | |
69 | Using the Zope traversal machinery, traverse to the path given by |
70 | @@ -125,6 +129,8 @@ class TraversalMixin: |
71 | :param use_default_referer: If True, set the referer attribute in the |
72 | request header to DEFAULT_REFERER = "http://launchpad.test" |
73 | (otherwise it remains as None) |
74 | + :param webservice: If True, use a webservice-like request rather |
75 | + than a normal test request. |
76 | :return: The object found. |
77 | """ |
78 | # XXX: What's the difference between first_segment and path? -- mbp |
79 | @@ -132,7 +138,9 @@ class TraversalMixin: |
80 | extra = {'PATH_INFO': urlappend('/%s' % first_segment, path)} |
81 | if use_default_referer: |
82 | extra['HTTP_REFERER'] = DEFAULT_REFERER |
83 | - request = LaunchpadTestRequest(**extra) |
84 | + request_factory = ( |
85 | + WebServiceTestRequest if webservice else LaunchpadTestRequest) |
86 | + request = request_factory(**extra) |
87 | segments = reversed(path.split('/')) |
88 | request.setTraversalStack(segments) |
89 | traverser = LaunchpadRootNavigation( |
90 | @@ -168,6 +176,9 @@ class TestBranchTraversal(TestCaseWithFactory, TraversalMixin): |
91 | # branch. |
92 | branch = self.factory.makeAnyBranch() |
93 | self.assertRedirects(branch.unique_name, canonical_url(branch)) |
94 | + self.assertRedirects( |
95 | + branch.unique_name, canonical_url(branch, rootsite='api'), |
96 | + webservice=True) |
97 | |
98 | def test_no_such_unique_name(self): |
99 | # Traversing to /+branch/<unique_name> where 'unique_name' is for a |
100 | @@ -346,10 +357,16 @@ class TestCodeTraversal(TestCaseWithFactory, TraversalMixin): |
101 | def test_project_bzr_branch(self): |
102 | branch = self.factory.makeAnyBranch() |
103 | self.assertRedirects(branch.unique_name, canonical_url(branch)) |
104 | + self.assertRedirects( |
105 | + branch.unique_name, canonical_url(branch, rootsite='api'), |
106 | + webservice=True) |
107 | |
108 | def test_project_git_branch(self): |
109 | git_repo = self.factory.makeGitRepository() |
110 | self.assertRedirects(git_repo.unique_name, canonical_url(git_repo)) |
111 | + self.assertRedirects( |
112 | + git_repo.unique_name, canonical_url(git_repo, rootsite='api'), |
113 | + webservice=True) |
114 | |
115 | def test_no_such_bzr_unique_name(self): |
116 | branch = self.factory.makeAnyBranch() |
117 | diff --git a/lib/lp/code/browser/tests/test_branchtraversal.py b/lib/lp/code/browser/tests/test_branchtraversal.py |
118 | index 98c9f75..c26a699 100644 |
119 | --- a/lib/lp/code/browser/tests/test_branchtraversal.py |
120 | +++ b/lib/lp/code/browser/tests/test_branchtraversal.py |
121 | @@ -16,10 +16,11 @@ from lp.registry.interfaces.personproduct import ( |
122 | IPersonProductFactory, |
123 | ) |
124 | from lp.services.webapp.publisher import canonical_url |
125 | -from lp.testing import ( |
126 | - FakeLaunchpadRequest, |
127 | - TestCaseWithFactory, |
128 | +from lp.services.webapp.servers import ( |
129 | + LaunchpadTestRequest, |
130 | + WebServiceTestRequest, |
131 | ) |
132 | +from lp.testing import TestCaseWithFactory |
133 | from lp.testing.layers import DatabaseFunctionalLayer |
134 | |
135 | |
136 | @@ -36,11 +37,11 @@ class TestPersonBranchTraversal(TestCaseWithFactory): |
137 | TestCaseWithFactory.setUp(self) |
138 | self.person = self.factory.makePerson() |
139 | |
140 | - def assertRedirects(self, segments, url): |
141 | - redirection = self.traverse(segments) |
142 | + def assertRedirects(self, segments, url, webservice=False): |
143 | + redirection = self.traverse(segments, webservice=webservice) |
144 | self.assertEqual(url, redirection.target) |
145 | |
146 | - def traverse(self, segments): |
147 | + def traverse(self, segments, webservice=False): |
148 | """Traverse to 'segments' using a 'PersonNavigation' object. |
149 | |
150 | Using the Zope traversal machinery, traverse to the path given by |
151 | @@ -48,11 +49,16 @@ class TestPersonBranchTraversal(TestCaseWithFactory): |
152 | 'person' attribute. |
153 | |
154 | :param segments: A list of path segments. |
155 | + :param webservice: If True, use a webservice-like request rather |
156 | + than a normal test request. |
157 | :return: The object found. |
158 | """ |
159 | stack = list(reversed(segments)) |
160 | name = stack.pop() |
161 | - request = FakeLaunchpadRequest(['~' + self.person.name], stack) |
162 | + request_factory = ( |
163 | + WebServiceTestRequest if webservice else LaunchpadTestRequest) |
164 | + request = request_factory() |
165 | + request.setTraversalStack(stack) |
166 | traverser = PersonNavigation(self.person, request) |
167 | return traverser.publishTraverse(request, name) |
168 | |
169 | @@ -60,11 +66,15 @@ class TestPersonBranchTraversal(TestCaseWithFactory): |
170 | branch = self.factory.makeProductBranch(owner=self.person) |
171 | segments = ['+branch', branch.product.name, branch.name] |
172 | self.assertRedirects(segments, canonical_url(branch)) |
173 | + self.assertRedirects( |
174 | + segments, canonical_url(branch, rootsite='api'), webservice=True) |
175 | |
176 | def test_redirect_junk_branch(self): |
177 | branch = self.factory.makePersonalBranch(owner=self.person) |
178 | segments = ['+branch', '+junk', branch.name] |
179 | self.assertRedirects(segments, canonical_url(branch)) |
180 | + self.assertRedirects( |
181 | + segments, canonical_url(branch, rootsite='api'), webservice=True) |
182 | |
183 | def test_redirect_branch_not_found(self): |
184 | self.assertRaises( |
185 | @@ -78,6 +88,10 @@ class TestPersonBranchTraversal(TestCaseWithFactory): |
186 | ['foo', branch.distroseries.name, branch.sourcepackagename.name, |
187 | branch.name], |
188 | canonical_url(branch)) |
189 | + self.assertRedirects( |
190 | + ['foo', branch.distroseries.name, branch.sourcepackagename.name, |
191 | + branch.name], |
192 | + canonical_url(branch, rootsite='api'), webservice=True) |
193 | |
194 | def test_junk_branch(self): |
195 | branch = self.factory.makePersonalBranch(owner=self.person) |
196 | @@ -134,8 +148,8 @@ class TestPersonProductBranchTraversal(TestCaseWithFactory): |
197 | """ |
198 | stack = list(reversed(segments)) |
199 | name = stack.pop() |
200 | - request = FakeLaunchpadRequest( |
201 | - ['~' + self.person.name, self.product.name], stack) |
202 | + request = LaunchpadTestRequest() |
203 | + request.setTraversalStack(stack) |
204 | traverser = PersonProductNavigation(self.person_product, request) |
205 | return traverser.publishTraverse(request, name) |
206 | |
207 | diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py |
208 | index facd4d2..193df25 100644 |
209 | --- a/lib/lp/registry/browser/person.py |
210 | +++ b/lib/lp/registry/browser/person.py |
211 | @@ -364,7 +364,8 @@ class BranchTraversalMixin: |
212 | """Redirect to canonical_url.""" |
213 | branch = getUtility(IBranchNamespaceSet).traverse(self._getSegments()) |
214 | if branch: |
215 | - return self.redirectSubTree(canonical_url(branch)) |
216 | + return self.redirectSubTree( |
217 | + canonical_url(branch, request=self.request)) |
218 | raise NotFoundError |
219 | |
220 | @stepthrough('+git') |
221 | @@ -414,7 +415,8 @@ class BranchTraversalMixin: |
222 | if using_pillar_alias: |
223 | # This repository was accessed through one of its project's |
224 | # aliases, so we must redirect to its canonical URL. |
225 | - return self.redirectSubTree(canonical_url(repository)) |
226 | + return self.redirectSubTree( |
227 | + canonical_url(repository, request=self.request)) |
228 | |
229 | return repository |
230 | except (NotFoundError, InvalidNamespace, InvalidProductName): |
231 | @@ -478,13 +480,15 @@ class BranchTraversalMixin: |
232 | if branch.product.name != pillar_name: |
233 | # This branch was accessed through one of its project's |
234 | # aliases, so we must redirect to its canonical URL. |
235 | - return self.redirectSubTree(canonical_url(branch)) |
236 | + return self.redirectSubTree( |
237 | + canonical_url(branch, request=self.request)) |
238 | |
239 | if branch.distribution is not None: |
240 | if branch.distribution.name != pillar_name: |
241 | # This branch was accessed through one of its distribution's |
242 | # aliases, so we must redirect to its canonical URL. |
243 | - return self.redirectSubTree(canonical_url(branch)) |
244 | + return self.redirectSubTree( |
245 | + canonical_url(branch, request=self.request)) |
246 | |
247 | return branch |
248 | |
249 | diff --git a/lib/lp/registry/browser/tests/test_person.py b/lib/lp/registry/browser/tests/test_person.py |
250 | index 2008a61..07ef8d4 100644 |
251 | --- a/lib/lp/registry/browser/tests/test_person.py |
252 | +++ b/lib/lp/registry/browser/tests/test_person.py |
253 | @@ -184,6 +184,32 @@ class TestPersonNavigation(TestCaseWithFactory): |
254 | repository.owner.name, project.name, repository.name) |
255 | self.assertEqual(repository, test_traverse(url)[0]) |
256 | |
257 | + def test_traverse_git_repository_project_alias(self): |
258 | + project = self.factory.makeProduct() |
259 | + alias = project.name + "-2" |
260 | + removeSecurityProxy(project).setAliases([alias]) |
261 | + repository = self.factory.makeGitRepository(target=project) |
262 | + url = "/~%s/%s/+git/%s" % ( |
263 | + repository.owner.name, alias, repository.name) |
264 | + expected_url = "http://code.launchpad.test/~%s/%s/+git/%s" % ( |
265 | + repository.owner.name, project.name, repository.name) |
266 | + _, view, _ = test_traverse(url) |
267 | + self.assertIsInstance(view, RedirectionView) |
268 | + self.assertEqual(expected_url, removeSecurityProxy(view).target) |
269 | + |
270 | + def test_traverse_git_repository_project_alias_api(self): |
271 | + project = self.factory.makeProduct() |
272 | + alias = project.name + "-2" |
273 | + removeSecurityProxy(project).setAliases([alias]) |
274 | + repository = self.factory.makeGitRepository(target=project) |
275 | + url = "http://api.launchpad.test/devel/~%s/%s/+git/%s" % ( |
276 | + repository.owner.name, alias, repository.name) |
277 | + expected_url = "http://api.launchpad.test/devel/~%s/%s/+git/%s" % ( |
278 | + repository.owner.name, project.name, repository.name) |
279 | + _, view, _ = test_traverse(url) |
280 | + self.assertIsInstance(view, RedirectionView) |
281 | + self.assertEqual(expected_url, removeSecurityProxy(view).target) |
282 | + |
283 | def test_traverse_git_repository_package(self): |
284 | dsp = self.factory.makeDistributionSourcePackage() |
285 | repository = self.factory.makeGitRepository(target=dsp) |
286 | @@ -192,6 +218,38 @@ class TestPersonNavigation(TestCaseWithFactory): |
287 | dsp.sourcepackagename.name, repository.name) |
288 | self.assertEqual(repository, test_traverse(url)[0]) |
289 | |
290 | + def test_traverse_git_repository_package_alias(self): |
291 | + dsp = self.factory.makeDistributionSourcePackage() |
292 | + alias = dsp.distribution.name + "-2" |
293 | + removeSecurityProxy(dsp.distribution).setAliases([alias]) |
294 | + repository = self.factory.makeGitRepository(target=dsp) |
295 | + url = "/~%s/%s/+source/%s/+git/%s" % ( |
296 | + repository.owner.name, alias, |
297 | + dsp.sourcepackagename.name, repository.name) |
298 | + expected_url = ( |
299 | + "http://code.launchpad.test/~%s/%s/+source/%s/+git/%s" % ( |
300 | + repository.owner.name, dsp.distribution.name, |
301 | + dsp.sourcepackagename.name, repository.name)) |
302 | + _, view, _ = test_traverse(url) |
303 | + self.assertIsInstance(view, RedirectionView) |
304 | + self.assertEqual(expected_url, removeSecurityProxy(view).target) |
305 | + |
306 | + def test_traverse_git_repository_package_alias_api(self): |
307 | + dsp = self.factory.makeDistributionSourcePackage() |
308 | + alias = dsp.distribution.name + "-2" |
309 | + removeSecurityProxy(dsp.distribution).setAliases([alias]) |
310 | + repository = self.factory.makeGitRepository(target=dsp) |
311 | + url = "http://api.launchpad.test/devel/~%s/%s/+source/%s/+git/%s" % ( |
312 | + repository.owner.name, alias, |
313 | + dsp.sourcepackagename.name, repository.name) |
314 | + expected_url = ( |
315 | + "http://api.launchpad.test/devel/~%s/%s/+source/%s/+git/%s" % ( |
316 | + repository.owner.name, dsp.distribution.name, |
317 | + dsp.sourcepackagename.name, repository.name)) |
318 | + _, view, _ = test_traverse(url) |
319 | + self.assertIsInstance(view, RedirectionView) |
320 | + self.assertEqual(expected_url, removeSecurityProxy(view).target) |
321 | + |
322 | def test_traverse_git_repository_oci_project(self): |
323 | oci_project = self.factory.makeOCIProject() |
324 | repository = self.factory.makeGitRepository(target=oci_project) |