Merge lp:~fabricematrat/charmworld/redirect-charm into lp:charmworld
- redirect-charm
- Merge into trunk
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 |
Related bugs: |
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:
|
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

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 |
Looks good Fabrice, thanks for the branch. A few suggestions.