Merge lp:~cjwatson/launchpad/global-bugs-search into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18386
Proposed branch: lp:~cjwatson/launchpad/global-bugs-search
Merge into: lp:launchpad
Diff against target: 287 lines (+124/-18)
7 files modified
lib/lp/bugs/feed/bug.py (+9/-3)
lib/lp/bugs/interfaces/malone.py (+6/-6)
lib/lp/bugs/stories/feeds/xx-bug-atom.txt (+38/-0)
lib/lp/bugs/stories/feeds/xx-bug-html.txt (+27/-0)
lib/lp/bugs/tests/test_searchtasks_webservice.py (+24/-1)
lib/lp/services/feeds/browser.py (+10/-3)
lib/lp/systemhomes.py (+10/-5)
To merge this branch: bzr merge lp:~cjwatson/launchpad/global-bugs-search
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+324122@code.launchpad.net

Commit message

Export MaloneApplication.searchTasks on the webservice, and implement a global bugs feed to go with it.

Description of the change

MaloneApplication was very close to being a valid IHasBugs implementation, so I just adjusted things a little until it actually is one.

I had to implement a global bugs feed as well, since adding IHasBugs caused the feeds mechanism to spring into action.

To post a comment you must log in.
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
=== modified file 'lib/lp/bugs/feed/bug.py'
--- lib/lp/bugs/feed/bug.py 2014-11-29 01:33:59 +0000
+++ lib/lp/bugs/feed/bug.py 2017-05-17 06:46:56 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Bug feed (syndication) views."""4"""Bug feed (syndication) views."""
@@ -228,7 +228,10 @@
228 @property228 @property
229 def title(self):229 def title(self):
230 """See `IFeed`."""230 """See `IFeed`."""
231 return "Bugs in %s" % self.context.displayname231 if IMaloneApplication.providedBy(self.context):
232 return "Launchpad bugs"
233 else:
234 return "Bugs in %s" % self.context.displayname
232235
233 @property236 @property
234 def feed_id(self):237 def feed_id(self):
@@ -240,7 +243,10 @@
240 datecreated = self.context.datecreated.date().isoformat()243 datecreated = self.context.datecreated.date().isoformat()
241 else:244 else:
242 datecreated = '2008'245 datecreated = '2008'
243 url_path = urlparse(self.link_alternate)[2]246 if IMaloneApplication.providedBy(self.context):
247 url_path = ''
248 else:
249 url_path = urlparse(self.link_alternate)[2]
244 id_ = 'tag:launchpad.net,%s:/%s%s' % (250 id_ = 'tag:launchpad.net,%s:/%s%s' % (
245 datecreated,251 datecreated,
246 self.rootsite,252 self.rootsite,
247253
=== modified file 'lib/lp/bugs/interfaces/malone.py'
--- lib/lp/bugs/interfaces/malone.py 2013-01-07 02:40:55 +0000
+++ lib/lp/bugs/interfaces/malone.py 2017-05-17 06:46:56 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Interfaces pertaining to the launchpad Malone application."""4"""Interfaces pertaining to the launchpad Malone application."""
@@ -20,7 +20,10 @@
20from zope.interface import Attribute20from zope.interface import Attribute
2121
22from lp.bugs.interfaces.bug import IBug22from lp.bugs.interfaces.bug import IBug
23from lp.bugs.interfaces.bugtarget import IBugTarget23from lp.bugs.interfaces.bugtarget import (
24 IBugTarget,
25 IHasBugs,
26 )
24from lp.services.webapp.interfaces import ILaunchpadApplication27from lp.services.webapp.interfaces import ILaunchpadApplication
2528
2629
@@ -30,13 +33,10 @@
30 ]33 ]
3134
3235
33class IMaloneApplication(ILaunchpadApplication):36class IMaloneApplication(ILaunchpadApplication, IHasBugs):
34 """Application root for malone."""37 """Application root for malone."""
35 export_as_webservice_collection(IBug)38 export_as_webservice_collection(IBug)
3639
37 def searchTasks(search_params):
38 """Search IBugTasks with the given search parameters."""
39
40 @call_with(user=REQUEST_USER)40 @call_with(user=REQUEST_USER)
41 @operation_parameters(41 @operation_parameters(
42 bug_id=copy_field(IBug['id']),42 bug_id=copy_field(IBug['id']),
4343
=== modified file 'lib/lp/bugs/stories/feeds/xx-bug-atom.txt'
--- lib/lp/bugs/stories/feeds/xx-bug-atom.txt 2016-07-27 22:36:34 +0000
+++ lib/lp/bugs/stories/feeds/xx-bug-atom.txt 2017-05-17 06:46:56 +0000
@@ -437,6 +437,44 @@
437 ... "Published dates are not sorted.")437 ... "Published dates are not sorted.")
438438
439439
440== Latest bugs for any target ==
441
442This feed gets the latest bugs reported against any target.
443
444 >>> browser.open('http://feeds.launchpad.dev/bugs/latest-bugs.atom')
445 >>> validate_feed(browser.contents,
446 ... browser.headers['content-type'], browser.url)
447 No Errors
448 >>> BSS(browser.contents).title.contents
449 [u'Launchpad bugs']
450 >>> browser.url
451 'http://feeds.launchpad.dev/bugs/latest-bugs.atom'
452
453 >>> soup = BSS(browser.contents, parseOnlyThese=SoupStrainer('id'))
454 >>> print extract_text(soup.find('id'))
455 tag:launchpad.net,2008:/bugs
456
457 >>> self_links = parse_links(browser.contents, 'self')
458 >>> for link in self_links:
459 ... print link
460 <link rel="self" href="http://feeds.launchpad.dev/bugs/latest-bugs.atom" />
461
462 >>> entries = parse_entries(browser.contents)
463 >>> print len(entries)
464 13
465
466 >>> entry = entries[0]
467 >>> print extract_text(entry.title)
468 [15] Nonsensical bugs are useless
469 >>> print extract_text(entry.author('name')[0])
470 Foo Bar
471 >>> print extract_text(entry.author('uri')[0])
472 http://bugs.launchpad.dev/~name16
473
474 >>> assert check_entries_order(entries), (
475 ... "Published dates are not sorted.")
476
477
440== General bug search ==478== General bug search ==
441479
442This feed is the most useful of them all. Any bug search can be turned into480This feed is the most useful of them all. Any bug search can be turned into
443481
=== modified file 'lib/lp/bugs/stories/feeds/xx-bug-html.txt'
--- lib/lp/bugs/stories/feeds/xx-bug-html.txt 2016-07-27 22:36:34 +0000
+++ lib/lp/bugs/stories/feeds/xx-bug-html.txt 2017-05-17 06:46:56 +0000
@@ -146,6 +146,33 @@
146 [15, 15, 12, 11, 10, 9, 9, 7, 7, 3, 3, 3]146 [15, 15, 12, 11, 10, 9, 9, 7, 7, 3, 3, 3]
147147
148148
149== Latest bugs for any target ==
150
151This feed gets the latest bugs reported against any target.
152
153 >>> browser.open('http://feeds.launchpad.dev/bugs/latest-bugs.html?'
154 ... 'show_column=bugtargetdisplayname')
155 >>> browser.title
156 'Launchpad bugs'
157 >>> browser.url
158 'http://feeds.launchpad.dev/bugs/latest-bugs.html?show_column=bugtargetdisplayname'
159
160 >>> entries = parse_entries(browser.contents)
161 >>> print len(entries)
162 27
163
164 >>> print_entry(entries[1])
165 number: 15
166 href: http://bugs.launchpad.dev/redfish/+bug/15
167 title: Nonsensical bugs are useless
168 importance: Undecided
169 status: New
170
171 >>> get_bug_numbers(entries)
172 [15, 15, 13, 12, 11, 10, 9, 9, 8, 7, 7, 5, 5, 5, 4, 3, 3, 3, 2, 2, 2, 2,
173 2, 1, 1, 1]
174
175
149== General bug search ==176== General bug search ==
150177
151This feed is the most useful of them all. Any bug search can be turned into178This feed is the most useful of them all. Any bug search can be turned into
152179
=== modified file 'lib/lp/bugs/tests/test_searchtasks_webservice.py'
--- lib/lp/bugs/tests/test_searchtasks_webservice.py 2012-10-09 10:28:02 +0000
+++ lib/lp/bugs/tests/test_searchtasks_webservice.py 2017-05-17 06:46:56 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Webservice unit tests related to Launchpad Bugs."""4"""Webservice unit tests related to Launchpad Bugs."""
@@ -124,6 +124,29 @@
124 self.assertEqual(response['total_size'], 2)124 self.assertEqual(response['total_size'], 2)
125125
126126
127class TestMaloneApplicationSearchTasks(TestCaseWithFactory):
128 """Test the searchTasks operation on the top-level /bugs collection."""
129
130 layer = DatabaseFunctionalLayer
131
132 def test_global_search_by_tag(self):
133 project1 = self.factory.makeProduct()
134 project2 = self.factory.makeProduct()
135 bug1 = self.factory.makeBug(target=project1, tags=["foo"])
136 self.factory.makeBug(target=project1, tags=["bar"])
137 bug3 = self.factory.makeBug(target=project2, tags=["foo"])
138 self.factory.makeBug(target=project2, tags=["baz"])
139 webservice = LaunchpadWebServiceCaller(
140 "launchpad-library", "salgado-change-anything")
141 response = webservice.named_get(
142 "/bugs", "searchTasks", api_version="devel", tags="foo").jsonBody()
143 self.assertEqual(2, response["total_size"])
144 self.assertContentEqual(
145 [bug1.id, bug3.id],
146 [int(entry["bug_link"].split("/")[-1])
147 for entry in response["entries"]])
148
149
127class TestGetBugData(TestCaseWithFactory):150class TestGetBugData(TestCaseWithFactory):
128 """Tests for the /bugs getBugData operation."""151 """Tests for the /bugs getBugData operation."""
129152
130153
=== modified file 'lib/lp/services/feeds/browser.py'
--- lib/lp/services/feeds/browser.py 2015-07-08 16:05:11 +0000
+++ lib/lp/services/feeds/browser.py 2017-05-17 06:46:56 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""View support classes for feeds."""4"""View support classes for feeds."""
@@ -35,6 +35,7 @@
35 IBugTask,35 IBugTask,
36 IBugTaskSet,36 IBugTaskSet,
37 )37 )
38from lp.bugs.interfaces.malone import IMaloneApplication
38from lp.code.interfaces.branch import IBranch39from lp.code.interfaces.branch import IBranch
39from lp.layers import FeedsLayer40from lp.layers import FeedsLayer
40from lp.registry.interfaces.announcement import (41from lp.registry.interfaces.announcement import (
@@ -121,7 +122,8 @@
121 redirect = RedirectionView(target, self.request, 301)122 redirect = RedirectionView(target, self.request, 301)
122 return redirect123 return redirect
123124
124 # Handle the two formats of urls:125 # Handle the three formats of urls:
126 # http://feeds.launchpad.net/bugs/latest-bugs.atom
125 # http://feeds.launchpad.net/bugs/+bugs.atom?...127 # http://feeds.launchpad.net/bugs/+bugs.atom?...
126 # http://feeds.launchpad.net/bugs/1/bug.atom128 # http://feeds.launchpad.net/bugs/1/bug.atom
127 if name == 'bugs':129 if name == 'bugs':
@@ -134,6 +136,8 @@
134 return getUtility(IBugTaskSet)136 return getUtility(IBugTaskSet)
135 else:137 else:
136 raise Unauthorized("Bug search feed deactivated")138 raise Unauthorized("Bug search feed deactivated")
139 elif bug_id.startswith('latest-bugs.'):
140 return getUtility(IMaloneApplication)
137 else:141 else:
138 self.request.stepstogo.consume()142 self.request.stepstogo.consume()
139 return getUtility(IBugSet).getByNameOrID(bug_id)143 return getUtility(IBugSet).getByNameOrID(bug_id)
@@ -209,7 +213,10 @@
209213
210 @property214 @property
211 def title(self):215 def title(self):
212 return 'Latest Bugs for %s' % self.context.displayname216 if IMaloneApplication.providedBy(self.context):
217 return 'Latest Bugs'
218 else:
219 return 'Latest Bugs for %s' % self.context.displayname
213220
214 @property221 @property
215 def href(self):222 def href(self):
216223
=== modified file 'lib/lp/systemhomes.py'
--- lib/lp/systemhomes.py 2015-07-08 16:05:11 +0000
+++ lib/lp/systemhomes.py 2017-05-17 06:46:56 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Content classes for the 'home pages' of the subsystems of Launchpad."""4"""Content classes for the 'home pages' of the subsystems of Launchpad."""
@@ -40,6 +40,7 @@
40 IPrivateMaloneApplication,40 IPrivateMaloneApplication,
41 )41 )
42from lp.bugs.model.bug import Bug42from lp.bugs.model.bug import Bug
43from lp.bugs.model.bugtarget import HasBugsBase
43from lp.code.interfaces.codehosting import (44from lp.code.interfaces.codehosting import (
44 IBazaarApplication,45 IBazaarApplication,
45 ICodehostingApplication,46 ICodehostingApplication,
@@ -117,14 +118,18 @@
117118
118119
119@implementer(IMaloneApplication)120@implementer(IMaloneApplication)
120class MaloneApplication:121class MaloneApplication(HasBugsBase):
121122
122 def __init__(self):123 def __init__(self):
123 self.title = 'Malone: the Launchpad bug tracker'124 self.title = 'Malone: the Launchpad bug tracker'
124125
125 def searchTasks(self, search_params):126 def _customizeSearchParams(self, search_params):
126 """See `IMaloneApplication`."""127 """See `HasBugsBase`."""
127 return getUtility(IBugTaskSet).search(search_params)128 pass
129
130 def getBugSummaryContextWhereClause(self):
131 """See `HasBugsBase`."""
132 return True
128133
129 def getBugData(self, user, bug_id, related_bug=None):134 def getBugData(self, user, bug_id, related_bug=None):
130 """See `IMaloneApplication`."""135 """See `IMaloneApplication`."""