Merge lp:~fabricematrat/charmworld/redirect-charm into lp:charmworld

Proposed by Fabrice Matrat
Status: Merged
Approved by: Brad Crittenden
Approved revision: 525
Merged at revision: 520
Proposed branch: lp:~fabricematrat/charmworld/redirect-charm
Merge into: lp:charmworld
Diff against target: 470 lines (+88/-225)
3 files modified
charmworld/views/charms.py (+42/-21)
charmworld/views/tests/test_charms.py (+42/-203)
default.ini (+4/-1)
To merge this branch: bzr merge lp:~fabricematrat/charmworld/redirect-charm
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Juju Gui Bot continuous-integration Approve
j.c.sackett (community) Approve
Review via email: mp+249038@code.launchpad.net

Commit message

Add a redirect for charms
R=bac, jcsackett

Description of the change

Add a redirect for charms

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Looks good Fabrice, thanks for the branch. A few suggestions.

review: Approve (code)
Revision history for this message
j.c.sackett (jcsackett) wrote :

Fabrice--

This looks alright. I have some concerns about the redirects, but I don't think there's a better solution at this time--possibly something worth filing issues against jujucharms.com for?

Revision history for this message
j.c.sackett (jcsackett) :
review: Approve
Revision history for this message
Juju Gui Bot (juju-gui-bot) :
review: Approve (continuous-integration)
Revision history for this message
Brad Crittenden (bac) wrote :

Even better

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmworld/views/charms.py'
2--- charmworld/views/charms.py 2014-04-02 14:12:22 +0000
3+++ charmworld/views/charms.py 2015-02-09 18:46:28 +0000
4@@ -9,6 +9,7 @@
5 import json
6 import os
7 import pymongo
8+from pyramid.httpexceptions import HTTPMovedPermanently
9 from pyramid.view import view_config
10 from webob import Response
11
12@@ -36,7 +37,6 @@
13 found,
14 interfaces,
15 name_filter,
16- sub_filter,
17 )
18
19
20@@ -246,42 +246,42 @@
21 route_name="charm-collection",
22 renderer="charmworld:templates/charm-collection.pt")
23 def charm_collection(request):
24- query, sub_only = sub_filter({}, request)
25- return found_charm_collection(
26- request.db, query, sub_only, only_promulgated=True)
27+ redirect_url = request.registry.settings.get('redirect_jujucharms')
28+ location = ('{url}/solutions'.format(url=redirect_url))
29+ raise HTTPMovedPermanently(location=location)
30
31
32 @cached_view_config(
33 route_name="personal-collection",
34 renderer="charmworld:templates/charm-collection.pt")
35 def personal_collection(request):
36- query, sub_only = sub_filter({
37- "owner": request.matchdict["owner"]
38- }, request)
39- return found_charm_collection(request.db, query, sub_only)
40+ redirect_url = request.registry.settings.get('redirect_jujucharms')
41+ location = ('{url}/q/{owner}'.format(url=redirect_url,
42+ owner=request.matchdict["owner"]))
43+ raise HTTPMovedPermanently(location=location)
44
45
46 @cached_view_config(
47 route_name="series-collection",
48 renderer="charmworld:templates/charm-collection.pt")
49 def series_collection(request):
50- query, sub_only = sub_filter(
51- {"series": request.matchdict["series"]}, request)
52- return found_charm_collection(
53- request.db, query, sub_only, only_promulgated=True)
54+ redirect_url = request.registry.settings.get('redirect_jujucharms')
55+ location = ('{url}/q/{series}'.format(url=redirect_url,
56+ series=request.matchdict["series"]))
57+ raise HTTPMovedPermanently(location=location)
58
59
60 @cached_view_config(
61 route_name="personal-series",
62 renderer="charmworld:templates/charm-collection.pt")
63 def personal_series_collection(request):
64- query, sub_only = sub_filter(
65- {
66- "owner": request.matchdict["owner"],
67- "series": request.matchdict["series"]
68- },
69- request)
70- return found_charm_collection(request.db, query, sub_only)
71+ redirect_url = request.registry.settings.get('redirect_jujucharms')
72+ location = ('{url}/q/{owner}/{series}'.format(
73+ url=redirect_url,
74+ owner=request.matchdict["owner"],
75+ series=request.matchdict["series"])
76+ )
77+ raise HTTPMovedPermanently(location=location)
78
79
80 @cached_view_config(
81@@ -292,7 +292,18 @@
82 renderer="charmworld:templates/charm.pt")
83 def personal_charm(request):
84 _reconcile_revision(request)
85- return _charm_view(request, find_charm(request))
86+ redirect_url = request.registry.settings.get('redirect_jujucharms')
87+ match = request.matchdict
88+ location = '{url}/u/{owner}/{charm}/{series}'.format(
89+ url=redirect_url,
90+ owner=match['owner'],
91+ charm=match['charm'],
92+ series=match['series']
93+ )
94+ if 'revision' in match:
95+ location = '{location}/{revision}'.format(location=location,
96+ revision=match['revision'])
97+ raise HTTPMovedPermanently(location=location)
98
99
100 @cached_view_config(
101@@ -316,7 +327,17 @@
102 renderer="charmworld:templates/charm.pt")
103 def distro_charm(request):
104 _reconcile_revision(request)
105- return _charm_view(request, find_charm(request, promulgated=True))
106+ redirect_url = request.registry.settings.get('redirect_jujucharms')
107+ match = request.matchdict
108+ location = '{url}/{charm}/{series}'.format(
109+ url=redirect_url,
110+ charm=match['charm'],
111+ series=match['series']
112+ )
113+ if 'revision' in match:
114+ location = '{location}/{revision}'.format(location=location,
115+ revision=match['revision'])
116+ raise HTTPMovedPermanently(location=location)
117
118
119 @cached_view_config(
120
121=== modified file 'charmworld/views/tests/test_charms.py'
122--- charmworld/views/tests/test_charms.py 2014-04-02 14:12:22 +0000
123+++ charmworld/views/tests/test_charms.py 2015-02-09 18:46:28 +0000
124@@ -4,6 +4,8 @@
125 from datetime import date
126 from logging import getLogger
127
128+from pyramid.httpexceptions import HTTPMovedPermanently
129+
130 from charmworld.jobs.ingest import (
131 add_files,
132 )
133@@ -131,189 +133,30 @@
134
135 def test_charm_collection_basic(self):
136 """Simple sanity check."""
137- one_id, one = factory.makeCharm(self.db, promulgated=True)
138- two_id, two = factory.makeCharm(self.db, promulgated=True, owner='foo')
139- three_id, three = factory.makeCharm(self.db, promulgated=False)
140-
141- response = charm_collection(self.getRequest())
142- charm_names = set(charm.name for charm in response['charms'])
143- expected = set((one['name'], two['name']))
144- self.assertEqual(expected, charm_names)
145-
146- def test_charm_collection_sub_filter(self):
147- """With the subordinate filter we limit out the non-sub charms."""
148- one_id, one = factory.makeCharm(
149- self.db, subordinate=True, promulgated=True)
150- two_id, two = factory.makeCharm(self.db, promulgated=True)
151-
152- request = self.getRequest()
153- request.GET['sub'] = 'true'
154- response = charm_collection(request)
155- charms = response['charms']
156- self.assertEqual(1, len(charms))
157- self.assertEqual(one['name'], charms[0].name)
158-
159- def test_charm_collection_charms_with_errors(self):
160- """Charms with errors are not returned by charm_collection()."""
161- one_id, one = factory.makeCharm(self.db, promulgated=True)
162- two_id, two = factory.makeCharm(
163- self.db, charm_error=True, promulgated=True)
164- response = charm_collection(self.getRequest())
165- charms = response['charms']
166- self.assertEqual(1, len(charms))
167- self.assertEqual(one['name'], charms[0].name)
168+ with self.assertRaises(HTTPMovedPermanently) as e:
169+ charm_collection(self.getRequest())
170+ self.assertIn('/solutions', e.exception.location)
171
172 def test_personal_collection(self):
173- # personal_collection() returns only charms owned by the given
174- # person.
175- one_id, one = factory.makeCharm(self.db, owner='foo')
176- two_id, two = factory.makeCharm(self.db, owner='bar')
177- request = self.getRequest()
178- request.matchdict = {'owner': 'foo'}
179- response = personal_collection(request)
180- charms = response['charms']
181- self.assertEqual(1, len(charms))
182- self.assertEqual('foo', charms[0].owner)
183-
184- def test_personal_collection_sub_filter(self):
185- # If the request parameter 'sub' is set, only subordinate charms
186- # are returned.
187- one_id, one = factory.makeCharm(self.db, owner='foo')
188- two_id, two = factory.makeCharm(self.db, owner='bar')
189- three_id, three = factory.makeCharm(
190- self.db, owner='foo', subordinate=True)
191- request = self.getRequest()
192- request.GET['sub'] = 'true'
193- request.matchdict = {'owner': 'foo'}
194- response = personal_collection(request)
195- charms = response['charms']
196- self.assertEqual(1, len(charms))
197- self.assertEqual(three['name'], charms[0].name)
198- # If the filter is no given in the request, all charms are returned.
199- request = self.getRequest()
200- request.matchdict = {'owner': 'foo'}
201- response = personal_collection(request)
202- charms = response['charms']
203- self.assertEqual(2, len(charms))
204- self.assertEqual('foo', charms[0].owner)
205- self.assertEqual('foo', charms[1].owner)
206-
207- def test_personal_collection_charms_with_errors(self):
208- # Charms with errors are not returned by personal_collection().
209- one_id, one = factory.makeCharm(self.db, owner='foo')
210- two_id, two = factory.makeCharm(self.db, owner='bar')
211- three_id, three = factory.makeCharm(
212- self.db, owner='foo', charm_error=True)
213- request = self.getRequest()
214- request.matchdict = {'owner': 'foo'}
215- response = personal_collection(request)
216- charms = response['charms']
217- self.assertEqual(1, len(charms))
218- self.assertEqual(one['name'], charms[0].name)
219+ request = self.getRequest()
220+ request.matchdict = {'owner': 'foo'}
221+ with self.assertRaises(HTTPMovedPermanently) as e:
222+ personal_collection(request)
223+ self.assertIn('/q/foo', e.exception.location)
224
225 def test_series_collection(self):
226- # series_collection() returns only promulgated charms for a
227- # given series.
228- one_id, one = factory.makeCharm(
229- self.db, series='one', promulgated=True, name='charm-a')
230- two_id, two = factory.makeCharm(
231- self.db, series='two', promulgated=True)
232- three_id, three = factory.makeCharm(
233- self.db, series='one')
234- four_id, four = factory.makeCharm(
235- self.db, series='one', promulgated=True, owner='foo',
236- name='charm-b')
237- request = self.getRequest()
238- request.matchdict = {'series': 'one'}
239- response = series_collection(request)
240- charms = response['charms']
241- self.assertEqual(
242- ['charm-a', 'charm-b'], [charm.name for charm in charms])
243- self.assertEqual('one', charms[0].series)
244-
245- def test_series_collection_sub_filter(self):
246- # If the request parameter 'sub' is set, only subordinate charms
247- # are returned.
248- one_id, one = factory.makeCharm(
249- self.db, series='one', promulgated=True)
250- two_id, two = factory.makeCharm(
251- self.db, series='two', promulgated=True)
252- three_is, three = factory.makeCharm(
253- self.db, series='one', subordinate=True, promulgated=True)
254- request = self.getRequest()
255- request.matchdict = {'series': 'one'}
256- request.GET['sub'] = 'true'
257- response = series_collection(request)
258- charms = response['charms']
259- self.assertEqual(1, len(charms))
260- self.assertEqual(three['name'], charms[0].name)
261-
262- def test_series_collection_charms_with_errors(self):
263- # Charms with errors are not returned by series_collection().
264- one_id, one = factory.makeCharm(
265- self.db, series='one', promulgated=True)
266- two_id, two = factory.makeCharm(
267- self.db, series='two', promulgated=True)
268- three_is, three = factory.makeCharm(
269- self.db, series='one', charm_error=True, promulgated=True)
270- request = self.getRequest()
271- request.matchdict = {'series': 'one'}
272- response = series_collection(request)
273- charms = response['charms']
274- self.assertEqual(1, len(charms))
275- self.assertEqual(one['name'], charms[0].name)
276+ request = self.getRequest()
277+ request.matchdict = {'series': 'one'}
278+ with self.assertRaises(HTTPMovedPermanently) as e:
279+ series_collection(request)
280+ self.assertIn('/q/one', e.exception.location)
281
282 def test_personal_series_collection(self):
283- # personal_series_collection() returns only charms for a given
284- # series and person.
285- one_id, one = factory.makeCharm(self.db, series='one', owner='foo')
286- two_id, two = factory.makeCharm(self.db, series='two', owner='foo')
287- three_id, three = factory.makeCharm(self.db, series='one', owner='bar')
288- request = self.getRequest()
289- request.matchdict = {
290- 'owner': 'foo',
291- 'series': 'one',
292- }
293- response = personal_series_collection(request)
294- charms = response['charms']
295- self.assertEqual(1, len(charms))
296- self.assertEqual(one['name'], charms[0].name)
297-
298- def test_personal_series_collection_sub_filter(self):
299- # If the parameter 'sub' is set, personal_series_collection()
300- # returns only subordinate charms for the given person ans series.
301- one_id, one = factory.makeCharm(self.db, series='one', owner='foo')
302- two_id, two = factory.makeCharm(self.db, series='two', owner='foo')
303- three_id, three = factory.makeCharm(self.db, series='one', owner='bar')
304- four_id, four = factory.makeCharm(
305- self.db, series='one', owner='foo', subordinate=True)
306- request = self.getRequest()
307- request.matchdict = {
308- 'owner': 'foo',
309- 'series': 'one',
310- }
311- request.GET['sub'] = 'true'
312- response = personal_series_collection(request)
313- charms = response['charms']
314- self.assertEqual(1, len(charms))
315- self.assertEqual(four['name'], charms[0].name)
316-
317- def test_personal_series_collection_charms_with_errors(self):
318- # Charms with errors are not returned by series_collection().
319- one_id, one = factory.makeCharm(self.db, series='one', owner='foo')
320- two_id, two = factory.makeCharm(self.db, series='two', owner='foo')
321- three_id, three = factory.makeCharm(self.db, series='one', owner='bar')
322- four_id, four = factory.makeCharm(
323- self.db, series='one', owner='foo', charm_error=True)
324- request = self.getRequest()
325- request.matchdict = {
326- 'owner': 'foo',
327- 'series': 'one',
328- }
329- response = personal_series_collection(request)
330- charms = response['charms']
331- self.assertEqual(1, len(charms))
332- self.assertEqual(one['name'], charms[0].name)
333+ request = self.getRequest()
334+ request.matchdict = {'series': 'one', 'owner': 'me'}
335+ with self.assertRaises(HTTPMovedPermanently) as e:
336+ personal_series_collection(request)
337+ self.assertIn('/q/me/one', e.exception.location)
338
339 def test_interface(self):
340 # interface() returns all charms requiring or providing a given
341@@ -429,12 +272,8 @@
342 'charm': 'sample_charm',
343 'series': 'precise',
344 }
345- response = distro_charm(request)
346- expected_keys = set((
347- 'charm', 'is_featured', 'format_change', 'format_proof', 'name',
348- 'others', 'project', 'qadata', 'readme', 'readme_format',
349- 'icon_path'))
350- self.assertEqual(expected_keys, set(response))
351+ with self.assertRaises(HTTPMovedPermanently):
352+ distro_charm(request)
353
354 def test_distro_charm_json(self):
355 ignore, charm = factory.makeCharm(
356@@ -542,9 +381,8 @@
357 two_id, two = factory.makeCharm(self.db, promulgated=True)
358 del two['summary']
359 self.db.charms.save(two)
360- # Test that the route works.
361- resp = self.app.get('/charms/precise', status=200)
362- self.assertIn(two['short_url'], resp.body)
363+ response = self.app.get('/charms/precise', status=301)
364+ self.assertIn('/q/precise', response.body)
365
366 def test_route_personal_charm_with_revision(self):
367 # The route /~owner/series/charm-1 finds charms using owner,
368@@ -552,8 +390,8 @@
369 # does not include the revision.
370 ignore, charm = factory.makeCharm(
371 self.db, owner='owner', series='series', name='charm', files={})
372- response = self.app.get('/~owner/series/charm-1', status=200)
373- self.assertIn('juju deploy cs:~owner/series/charm', response.body)
374+ response = self.app.get('/~owner/series/charm-1', status=301)
375+ self.assertIn('/u/owner/charm/series/1', response.body)
376
377 def test_route_personal_charm_with_dash_and_revision(self):
378 # /~owner/series/charm-name-1 route find the charm.
379@@ -562,16 +400,17 @@
380 ignore, charm = factory.makeCharm(
381 self.db, owner='owner', series='series', name='charm-name',
382 files={})
383- response = self.app.get('/~owner/series/charm-name-1', status=200)
384- self.assertIn('juju deploy cs:~owner/series/charm-name', response.body)
385+ response = self.app.get('/~owner/series/charm-name-1', status=301)
386+ self.assertIn('/u/owner/charm-name/series/1', response.body)
387
388 def test_route_personal_charm_with_head_revision(self):
389 # the /~owner/series/charm-name-HEAD route finds the charm.
390 # The juju-gui apache rewrite versionless charm urls to help the GUI.
391 ignore, charm = factory.makeCharm(
392 self.db, owner='owner', series='series', name='charm', files={})
393- response = self.app.get('/~owner/series/charm-HEAD', status=200)
394- self.assertIn('juju deploy cs:~owner/series/charm', response.body)
395+ response = self.app.get('/~owner/series/charm-HEAD', status=301)
396+ self.assertIn('/u/owner/charm/series', response.body)
397+ self.assertNotIn('HEAD', response.body)
398
399 def test_route_personal_charm_without_revision(self):
400 # The route /~owner/series/charm route finds the charm using owner
401@@ -579,8 +418,8 @@
402 # matches the route.
403 ignore, charm = factory.makeCharm(
404 self.db, owner='owner', series='series', name='charm', files={})
405- response = self.app.get('/~owner/series/charm', status=200)
406- self.assertIn('juju deploy cs:~owner/series/charm', response.body)
407+ response = self.app.get('/~owner/series/charm', status=301)
408+ self.assertIn('/u/owner/charm/series', response.body)
409
410 def test_route_personal_charm_json_with_revision(self):
411 # The route /~owner/series/charm-1/json finds charms using owner,
412@@ -604,10 +443,10 @@
413 # The route /series/charm-1 route finds promulgated charms using
414 # The series, charm name and version. The deploy instruction does not
415 # include the version.
416- ignore, charm = factory.makeCharm(
417+ factory.makeCharm(
418 self.db, promulgated=True, series='series', name='charm', files={})
419- response = self.app.get('/series/charm-1', status=200)
420- self.assertIn('juju deploy cs:series/charm', response.body)
421+ response = self.app.get('/series/charm-1', status=301)
422+ self.assertIn('/charm/series/1', response.body)
423
424 def test_route_promulgated_charm_with_head_revision(self):
425 # The route /series/charm-HEAD finds promulgated charms using
426@@ -615,22 +454,22 @@
427 # in the deploy instruction.
428 ignore, charm = factory.makeCharm(
429 self.db, promulgated=True, series='series', name='charm', files={})
430- response = self.app.get('/series/charm-HEAD', status=200)
431- self.assertIn('juju deploy cs:series/charm', response.body)
432+ response = self.app.get('/series/charm-HEAD', status=301)
433+ self.assertNotIn('HEAD', response.body)
434+ self.assertIn('/charm/series', response.body)
435
436 def test_route_promulgated_charm_without_revision(self):
437 # The route /series/charm finds the promulgated charms using just
438 # the series and charm name. The deploy instruction matches the route.
439- ignore, charm = factory.makeCharm(
440+ factory.makeCharm(
441 self.db, promulgated=True, series='series', name='charm', files={})
442- response = self.app.get('/series/charm', status=200)
443- self.assertIn('juju deploy cs:series/charm', response.body)
444+ self.app.get('/series/charm', status=301)
445
446 def test_charm_short_url_route(self):
447 ignore, charm_data = factory.makeCharm(
448 self.db, promulgated=True, series='series', name='charm', files={})
449 charm = Charm(charm_data)
450- self.app.get(charm.short_url, status=200)
451+ self.app.get(charm.short_url, status=301)
452
453
454 class TestQAForm(ViewTestBase):
455
456=== modified file 'default.ini'
457--- default.ini 2014-01-14 14:37:56 +0000
458+++ default.ini 2015-02-09 18:46:28 +0000
459@@ -66,7 +66,10 @@
460 run_test_builds = false
461 charm_test_url =
462 charm_test_token =
463-charm_test_job =
464+charm_test_job =
465+
466+# New web site for redirection
467+redirect_jujucharms = https://jujucharms.com
468
469 [server:main]
470 use = egg:Paste#http

Subscribers

People subscribed via source and target branches

to all changes: