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

Proposed by Fabrice Matrat
Status: Merged
Approved by: Fabrice Matrat
Approved revision: 521
Merged at revision: 521
Proposed branch: lp:~fabricematrat/charmworld/redirect-left
Merge into: lp:charmworld
Diff against target: 289 lines (+38/-162)
5 files modified
charmworld/views/misc.py (+6/-17)
charmworld/views/search.py (+9/-36)
charmworld/views/tests/test_auth.py (+2/-9)
charmworld/views/tests/test_misc.py (+0/-27)
charmworld/views/tests/test_search.py (+21/-73)
To merge this branch: bzr merge lp:~fabricematrat/charmworld/redirect-left
Reviewer Review Type Date Requested Status
Juju Gui Bot continuous-integration Approve
Brad Crittenden (community) code, qa Approve
Francesco Banconi Approve
Review via email: mp+249169@code.launchpad.net

Commit message

Added redirect for search, home, charmers

Description of the change

Added redirection for home, search, charmers

To post a comment you must log in.
Revision history for this message
Francesco Banconi (frankban) wrote :

Looks good, thank you.

review: Approve
Revision history for this message
Francesco Banconi (frankban) :
Revision history for this message
Brad Crittenden (bac) wrote :

Nice, no QA yet.

review: Approve (code)
Revision history for this message
Brad Crittenden (bac) wrote :

QA-not-OK. The /charmers redirect appears to be broken, unless I am exercising it incorrectly.

review: Needs Fixing (qa)
Revision history for this message
Brad Crittenden (bac) wrote :

QA-OK. Thanks for pointing me to the new endpoint.

review: Approve (code, qa)
Revision history for this message
Juju Gui Bot (juju-gui-bot) wrote :

FAILED: Autolanding.
No commit message was specified in the merge proposal. Hit 'Add commit message' on the merge proposal web page or follow the link below. You can approve the merge proposal yourself to rerun.
https://code.launchpad.net/~fabricematrat/charmworld/redirect-left/+merge/249169/+edit-commit-message

review: Needs Fixing (continuous-integration)
Revision history for this message
Juju Gui Bot (juju-gui-bot) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmworld/views/misc.py'
2--- charmworld/views/misc.py 2013-11-13 12:48:24 +0000
3+++ charmworld/views/misc.py 2015-02-10 11:13:22 +0000
4@@ -1,8 +1,6 @@
5 # Copyright 2012, 2013 Canonical Ltd. This software is licensed under the
6 # GNU Affero General Public License version 3 (see the file LICENSE).
7-
8-from collections import defaultdict
9-
10+from pyramid.httpexceptions import HTTPMovedPermanently
11 from pyramid.view import view_config
12
13 from charmworld import cached_view_config
14@@ -27,26 +25,17 @@
15 route_name="home",
16 renderer="charmworld:templates/index.pt")
17 def home(request):
18- return {}
19+ redirect_url = request.registry.settings.get('redirect_jujucharms')
20+ raise HTTPMovedPermanently(location=redirect_url)
21
22
23 @view_config(
24 route_name="charmers",
25 renderer="charmworld:templates/charmers.pt")
26 def charmers(request):
27- # We want to collect which users have contributes on which charms.
28- # This however reports charm owners ordered decending by ownership.
29- # The largest owner is not included because we think this is ~charmers.
30- results = request.db.charms.find(
31- {}, {"name": 1, 'owner': 1, 'short_url': 1})
32-
33- owner_map = defaultdict(int)
34- for i in results:
35- owner_map[i['owner']] += 1
36- owners = sorted(
37- owner_map.items(), key=lambda i: (i[1], i[0]), reverse=True)
38- owners.pop(0)
39- return {"owners": owners}
40+ redirect_url = request.registry.settings.get('redirect_jujucharms')
41+ location = '{url}/community/charmers'.format(url=redirect_url)
42+ raise HTTPMovedPermanently(location=location)
43
44
45 @view_config(
46
47=== modified file 'charmworld/views/search.py'
48--- charmworld/views/search.py 2014-03-18 15:20:47 +0000
49+++ charmworld/views/search.py 2015-02-10 11:13:22 +0000
50@@ -1,46 +1,19 @@
51 # Copyright 2012, 2013 Canonical Ltd. This software is licensed under the
52 # GNU Affero General Public License version 3 (see the file LICENSE).
53-from pyelasticsearch.exceptions import ElasticHttpError
54-from pyramid.httpexceptions import HTTPFound
55-from pyramid.renderers import render_to_response
56+from pyramid.httpexceptions import HTTPMovedPermanently
57 from pyramid.view import view_config
58
59-from charmworld.search import (
60- IndexNotReady,
61- SearchServiceNotAvailable,
62-)
63-from charmworld.views import log
64-
65-
66-def do_search(request):
67- text = request.params["search_text"]
68- return request.index_client.search(text)
69-
70
71 @view_config(
72 route_name="search",
73 renderer="charmworld:templates/search.pt")
74 def search(request):
75+ redirect_url = request.registry.settings.get('redirect_jujucharms')
76+ location = '{url}/q'.format(url=redirect_url)
77 if not "search_text" in request.params:
78- url = request.route_url("home")
79- return HTTPFound(location=url)
80- try:
81- search = do_search(request)
82- except (IndexNotReady, SearchServiceNotAvailable):
83- response = render_to_response('../templates/search-not-ready.pt', {},
84- request=request)
85- response.status_code = 503
86- return response
87- except ElasticHttpError as exc:
88- text = request.params["search_text"]
89- log.warning(
90- "User search error with search text='{}'".format(text))
91- log.exception(exc)
92- response = render_to_response(
93- '../templates/search-terms-bad.pt',
94- {'search_text': text},
95- request=request)
96- response.status_code = 400
97- return response
98- return dict((key, value) for key, value in search.items()
99- if key != 'matches')
100+ raise HTTPMovedPermanently(location=location)
101+ text = request.params["search_text"].strip()
102+ text = '/'.join(text.split())
103+ location = '{location}/{search}'.format(location=location,
104+ search=text)
105+ raise HTTPMovedPermanently(location=location)
106
107=== modified file 'charmworld/views/tests/test_auth.py'
108--- charmworld/views/tests/test_auth.py 2013-09-04 14:48:30 +0000
109+++ charmworld/views/tests/test_auth.py 2015-02-10 11:13:22 +0000
110@@ -40,19 +40,12 @@
111
112 def test_starts_with_login(self):
113 """Without being logged in you get the login nav link."""
114- resp = self.app.get('/', status=200)
115-
116- body = resp.body
117- self.assertIn("/login/openid", body)
118- self.assertNotIn("/logout", body)
119+ self.app.get('/', status=301)
120
121 def test_after_login_see_logout(self):
122 """Once logged in you get the logout nav link."""
123 with login_request(self.app) as resp:
124- resp.goto('/', status=200)
125- body = resp.body
126- self.assertNotIn("/login/openid", body)
127- self.assertIn("/logout", body)
128+ resp.goto('/', status=301)
129
130 def test_login_route(self):
131 path = '/login/openid'
132
133=== modified file 'charmworld/views/tests/test_misc.py'
134--- charmworld/views/tests/test_misc.py 2013-11-14 13:38:24 +0000
135+++ charmworld/views/tests/test_misc.py 2015-02-10 11:13:22 +0000
136@@ -2,7 +2,6 @@
137 # GNU Affero General Public License version 3 (see the file LICENSE).
138
139 import os
140-import re
141 import unittest
142
143 from mock import patch
144@@ -74,32 +73,6 @@
145 else:
146 self.fail('Cache-Control header not found.')
147
148- def test_main_template_scheme(self):
149- """Image and stylesheet URLs respect the WSGI scheme."""
150- src_pattern = re.compile('src="([a-z]+)://')
151- css_pattern = re.compile('href="([a-z]+)://.*\.css.*"')
152- # Https enabled.
153- response = self.app.get(
154- '/', extra_environ={'wsgi.url_scheme': 'https'})
155- https_count = 0
156- for match in src_pattern.finditer(response.ubody):
157- https_count += 1
158- self.assertEqual('https', match.group(1))
159- for match in css_pattern.finditer(response.ubody):
160- https_count += 1
161- self.assertEqual('https', match.group(1))
162- self.assertTrue(https_count >= 2)
163- # Http default.
164- response = self.app.get('/')
165- http_count = 0
166- for match in src_pattern.finditer(response.ubody):
167- http_count += 1
168- self.assertEqual('http', match.group(1))
169- for match in css_pattern.finditer(response.ubody):
170- http_count += 1
171- self.assertEqual('http', match.group(1))
172- self.assertTrue(https_count == http_count)
173-
174
175 class HeartbeatWebTestCase(WebTestBase):
176
177
178=== modified file 'charmworld/views/tests/test_search.py'
179--- charmworld/views/tests/test_search.py 2013-11-15 20:00:35 +0000
180+++ charmworld/views/tests/test_search.py 2015-02-10 11:13:22 +0000
181@@ -1,14 +1,8 @@
182 # Copyright 2012, 2013 Canonical Ltd. This software is licensed under the
183 # GNU Affero General Public License version 3 (see the file LICENSE).
184
185-from mock import patch
186-from pyelasticsearch import ElasticSearch
187-from pyelasticsearch.exceptions import ElasticHttpError
188+from pyramid.httpexceptions import HTTPMovedPermanently
189
190-from charmworld.search import (
191- ElasticSearchClient,
192- IndexNotReady,
193-)
194 from charmworld.testing import (
195 ViewTestBase,
196 WebTestBase,
197@@ -26,72 +20,26 @@
198
199 class SearchTests(ViewTestBase):
200
201- @patch('charmworld.views.search.do_search',
202- autospec=True, return_value=RETURN_VAL)
203- def test_search(self, mock):
204- terms = {"search_text": 'foo'}
205- keys = ['result_total', 'results', 'search_time']
206- request = self.getRequest(params=terms)
207- response = search.search(request)
208-
209- mock.assert_called_with(request)
210- self.assertEqual(keys, sorted(response.keys()))
211- self.assertEqual(0, response['result_total'])
212- self.assertEqual([], response['results'])
213- self.assertEqual(0.0, response['search_time'])
214-
215- @patch('charmworld.views.search.do_search',
216- autospec=True, side_effect=IndexNotReady)
217- def test_search_503(self, mock):
218- terms = {"search_text": 'foo'}
219- request = self.getRequest(params=terms)
220- # render_to_response requires the request to be much more like a real
221- # request, with scheme and static stuff handling, so mock it out.
222- with patch('charmworld.views.search.render_to_response') as render:
223- response = search.search(request)
224- render.assert_called_with('../templates/search-not-ready.pt', {},
225- request=request)
226- self.assertEqual(503, response.status_code)
227-
228- def test_search_503_on_connection_error(self):
229- terms = {"search_text": 'foo'}
230- request = self.getRequest(params=terms)
231- request.index_client = ElasticSearchClient(
232- ElasticSearch(['http://localhost:70']), 'foo')
233- # render_to_response requires the request to be much more like a real
234- # request, with scheme and static stuff handling, so mock it out.
235- with patch('charmworld.views.search.render_to_response') as render:
236- response = search.search(request)
237- render.assert_called_with('../templates/search-not-ready.pt', {},
238- request=request)
239- self.assertEqual(503, response.status_code)
240-
241- def test_tildes_not_in_start_are_unchanged(self):
242- terms = {"search_text": 'fo~o'}
243- request = self.getRequest(params=terms)
244- request.index_client = ElasticSearchClient(
245- ElasticSearch(['http://localhost:70']), '')
246- with patch.object(request.index_client, 'search') as req_search:
247- search.search(request)
248- req_search.assert_called_with('fo~o')
249-
250- def test_otherwise_tilde_causes_400(self):
251- terms = {"search_text": 'fo~o'}
252- request = self.getRequest(params=terms)
253- request.index_client = ElasticSearchClient(
254- ElasticSearch(['http://localhost:70']), '')
255-
256- def mock_search(request):
257- raise ElasticHttpError('oops')
258-
259- with patch.object(request.index_client, 'search', mock_search):
260- with patch('charmworld.views.search.render_to_response') as render:
261- response = search.search(request)
262- render.assert_called_with(
263- '../templates/search-terms-bad.pt',
264- {'search_text': 'fo~o'},
265- request=request)
266- self.assertEqual(400, response.status_code)
267+ def test_search(self):
268+ terms = {"search_text": 'foo'}
269+ request = self.getRequest(params=terms)
270+ with self.assertRaises(HTTPMovedPermanently) as e:
271+ search.search(request)
272+ self.assertIn('/q/foo', e.exception.location)
273+
274+ def test_search_multiple(self):
275+ terms = {"search_text": 'foo bar'}
276+ request = self.getRequest(params=terms)
277+ with self.assertRaises(HTTPMovedPermanently) as e:
278+ search.search(request)
279+ self.assertIn('/q/foo/bar', e.exception.location)
280+
281+ def test_search_with_space(self):
282+ terms = {"search_text": ' foo bar '}
283+ request = self.getRequest(params=terms)
284+ with self.assertRaises(HTTPMovedPermanently) as e:
285+ search.search(request)
286+ self.assertIn('/q/foo/bar', e.exception.location)
287
288
289 class SearchJSONTests(WebTestBase):

Subscribers

People subscribed via source and target branches

to all changes: