Merge ~cjwatson/launchpad:series-urls into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: d78275253f6763d50b0ca1c275eaff1b031f3b33
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:series-urls
Merge into: launchpad:master
Diff against target: 220 lines (+103/-9)
4 files modified
lib/lp/registry/browser/distribution.py (+16/-5)
lib/lp/registry/browser/product.py (+6/-1)
lib/lp/registry/browser/tests/test_distribution.py (+49/-1)
lib/lp/registry/browser/tests/test_product.py (+32/-2)
Reviewer Review Type Date Requested Status
William Grant code Approve
Ioana Lasc (community) Approve
Kristian Glass (community) Approve
Review via email: mp+385489@code.launchpad.net

Commit message

Add alternate <pillar>/+series/<name> URLs

Description of the change

These may be used instead of <pillar>/<name> URLs. For now they just redirect to the classic URLs, but that may change later.

To post a comment you must log in.
Revision history for this message
Kristian Glass (doismellburning) :
review: Approve
Revision history for this message
Ioana Lasc (ilasc) wrote :

looks good

review: Approve
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/registry/browser/distribution.py b/lib/lp/registry/browser/distribution.py
2index ffabedb..29eab7a 100644
3--- a/lib/lp/registry/browser/distribution.py
4+++ b/lib/lp/registry/browser/distribution.py
5@@ -121,7 +121,6 @@ from lp.services.webapp import (
6 canonical_url,
7 ContextMenu,
8 enabled_with_permission,
9- GetitemNavigation,
10 LaunchpadView,
11 Link,
12 Navigation,
13@@ -140,7 +139,7 @@ from lp.soyuz.interfaces.archive import IArchiveSet
14
15
16 class DistributionNavigation(
17- GetitemNavigation, BugTargetTraversalMixin, QuestionTargetTraversalMixin,
18+ Navigation, BugTargetTraversalMixin, QuestionTargetTraversalMixin,
19 FAQTargetNavigationMixin, StructuralSubscriptionTargetTraversalMixin,
20 PillarNavigationMixin, TargetDefaultVCSNavigationMixin):
21
22@@ -178,12 +177,24 @@ class DistributionNavigation(
23 def traverse_archive(self, name):
24 return self.context.getArchive(name)
25
26- def traverse(self, name):
27+ def _resolveSeries(self, name):
28 try:
29- return super(DistributionNavigation, self).traverse(name)
30+ return self.context[name], False
31 except NotFoundError:
32 resolved = self.context.resolveSeriesAlias(name)
33- return self.redirectSubTree(canonical_url(resolved), status=303)
34+ return resolved, True
35+
36+ @stepthrough('+series')
37+ def traverse_series(self, name):
38+ series, _ = self._resolveSeries(name)
39+ return self.redirectSubTree(canonical_url(series), status=303)
40+
41+ def traverse(self, name):
42+ series, is_alias = self._resolveSeries(name)
43+ if is_alias:
44+ return self.redirectSubTree(canonical_url(series), status=303)
45+ else:
46+ return series
47
48
49 class DistributionSetNavigation(Navigation):
50diff --git a/lib/lp/registry/browser/product.py b/lib/lp/registry/browser/product.py
51index c059dbb..5c3ebce 100644
52--- a/lib/lp/registry/browser/product.py
53+++ b/lib/lp/registry/browser/product.py
54@@ -1,4 +1,4 @@
55-# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
56+# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
57 # GNU Affero General Public License version 3 (see the file LICENSE).
58
59 """Browser views for products."""
60@@ -275,6 +275,11 @@ class ProductNavigation(
61 def traverse_commercialsubscription(self, name):
62 return self.context.commercial_subscription
63
64+ @stepthrough('+series')
65+ def traverse_series(self, name):
66+ series = self.context.getSeries(name)
67+ return self.redirectSubTree(canonical_url(series), status=303)
68+
69 def traverse(self, name):
70 return self.context.getSeries(name)
71
72diff --git a/lib/lp/registry/browser/tests/test_distribution.py b/lib/lp/registry/browser/tests/test_distribution.py
73index 0979ec2..f7f37fd 100644
74--- a/lib/lp/registry/browser/tests/test_distribution.py
75+++ b/lib/lp/registry/browser/tests/test_distribution.py
76@@ -17,11 +17,13 @@ from testtools.matchers import (
77 Not,
78 )
79 from zope.schema.vocabulary import SimpleVocabulary
80+from zope.security.proxy import removeSecurityProxy
81
82 from lp.app.browser.lazrjs import vocabulary_to_choice_edit_items
83 from lp.registry.enums import EXCLUSIVE_TEAM_POLICY
84 from lp.registry.interfaces.series import SeriesStatus
85 from lp.services.webapp import canonical_url
86+from lp.services.webapp.publisher import RedirectionView
87 from lp.testing import (
88 admin_logged_in,
89 BrowserTestCase,
90@@ -30,15 +32,61 @@ from lp.testing import (
91 record_two_runs,
92 TestCaseWithFactory,
93 )
94-from lp.testing.layers import DatabaseFunctionalLayer
95+from lp.testing.layers import (
96+ DatabaseFunctionalLayer,
97+ ZopelessDatabaseLayer,
98+ )
99 from lp.testing.pages import (
100 extract_text,
101 find_tag_by_id,
102 find_tags_by_class,
103 )
104+from lp.testing.publication import test_traverse
105 from lp.testing.views import create_initialized_view
106
107
108+class TestDistributionNavigation(TestCaseWithFactory):
109+
110+ layer = ZopelessDatabaseLayer
111+
112+ def assertRedirects(self, url, expected_url):
113+ _, view, _ = test_traverse(url)
114+ self.assertIsInstance(view, RedirectionView)
115+ self.assertEqual(expected_url, removeSecurityProxy(view).target)
116+
117+ def test_classic_series_url(self):
118+ distroseries = self.factory.makeDistroSeries()
119+ obj, _, _ = test_traverse(
120+ "http://launchpad.test/%s/%s" % (
121+ distroseries.distribution.name, distroseries.name))
122+ self.assertEqual(distroseries, obj)
123+
124+ def test_classic_series_url_with_alias(self):
125+ distroseries = self.factory.makeDistroSeries()
126+ distroseries.distribution.development_series_alias = "devel"
127+ self.assertRedirects(
128+ "http://launchpad.test/%s/devel" % distroseries.distribution.name,
129+ "http://launchpad.test/%s/%s" % (
130+ distroseries.distribution.name, distroseries.name))
131+
132+ def test_new_series_url_redirects(self):
133+ distroseries = self.factory.makeDistroSeries()
134+ self.assertRedirects(
135+ "http://launchpad.test/%s/+series/%s" % (
136+ distroseries.distribution.name, distroseries.name),
137+ "http://launchpad.test/%s/%s" % (
138+ distroseries.distribution.name, distroseries.name))
139+
140+ def test_new_series_url_with_alias_redirects(self):
141+ distroseries = self.factory.makeDistroSeries()
142+ distroseries.distribution.development_series_alias = "devel"
143+ self.assertRedirects(
144+ "http://launchpad.test/%s/+series/devel" % (
145+ distroseries.distribution.name),
146+ "http://launchpad.test/%s/%s" % (
147+ distroseries.distribution.name, distroseries.name))
148+
149+
150 class TestDistributionPage(TestCaseWithFactory):
151 """A TestCase for the distribution index page."""
152
153diff --git a/lib/lp/registry/browser/tests/test_product.py b/lib/lp/registry/browser/tests/test_product.py
154index 4271302..cbf03bc 100644
155--- a/lib/lp/registry/browser/tests/test_product.py
156+++ b/lib/lp/registry/browser/tests/test_product.py
157@@ -1,4 +1,4 @@
158-# Copyright 2010-2019 Canonical Ltd. This software is licensed under the
159+# Copyright 2010-2020 Canonical Ltd. This software is licensed under the
160 # GNU Affero General Public License version 3 (see the file LICENSE).
161
162 """Tests for product views."""
163@@ -52,7 +52,10 @@ from lp.registry.interfaces.product import (
164 from lp.registry.model.product import Product
165 from lp.services.config import config
166 from lp.services.database.interfaces import IStore
167-from lp.services.webapp.publisher import canonical_url
168+from lp.services.webapp.publisher import (
169+ canonical_url,
170+ RedirectionView,
171+ )
172 from lp.services.webapp.vhosts import allvhosts
173 from lp.testing import (
174 BrowserTestCase,
175@@ -66,12 +69,14 @@ from lp.testing.fixture import DemoMode
176 from lp.testing.layers import (
177 DatabaseFunctionalLayer,
178 LaunchpadFunctionalLayer,
179+ ZopelessDatabaseLayer,
180 )
181 from lp.testing.matchers import HasQueryCount
182 from lp.testing.pages import (
183 extract_text,
184 find_tag_by_id,
185 )
186+from lp.testing.publication import test_traverse
187 from lp.testing.service_usage_helpers import set_service_usage
188 from lp.testing.views import (
189 create_initialized_view,
190@@ -79,6 +84,31 @@ from lp.testing.views import (
191 )
192
193
194+class TestProductNavigation(TestCaseWithFactory):
195+
196+ layer = ZopelessDatabaseLayer
197+
198+ def assertRedirects(self, url, expected_url):
199+ _, view, _ = test_traverse(url)
200+ self.assertIsInstance(view, RedirectionView)
201+ self.assertEqual(expected_url, removeSecurityProxy(view).target)
202+
203+ def test_classic_series_url(self):
204+ productseries = self.factory.makeProductSeries()
205+ obj, _, _ = test_traverse(
206+ "http://launchpad.test/%s/%s" % (
207+ productseries.product.name, productseries.name))
208+ self.assertEqual(productseries, obj)
209+
210+ def test_new_series_url_redirects(self):
211+ productseries = self.factory.makeProductSeries()
212+ self.assertRedirects(
213+ "http://launchpad.test/%s/+series/%s" % (
214+ productseries.product.name, productseries.name),
215+ "http://launchpad.test/%s/%s" % (
216+ productseries.product.name, productseries.name))
217+
218+
219 class TestProductConfiguration(BrowserTestCase):
220 """Tests the configuration links and helpers."""
221

Subscribers

People subscribed via source and target branches

to status/vote changes: