Merge lp:~gmb/launchpad/subscribers-timeout-bug-471974 into lp:launchpad
- subscribers-timeout-bug-471974
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Graham Binns |
Approved revision: | not available |
Merged at revision: | not available |
Proposed branch: | lp:~gmb/launchpad/subscribers-timeout-bug-471974 |
Merge into: | lp:launchpad |
Diff against target: |
255 lines (+122/-17) 7 files modified
lib/canonical/launchpad/javascript/bugs/bugtask-index.js (+60/-9) lib/lp/bugs/browser/bug.py (+0/-6) lib/lp/bugs/browser/bugsubscription.py (+14/-0) lib/lp/bugs/browser/configure.zcml (+5/-0) lib/lp/bugs/browser/tests/bug-subscription-views.txt (+40/-0) lib/lp/bugs/templates/bug-portlet-subscribers.pt (+3/-0) lib/lp/bugs/templates/bugtask-index.pt (+0/-2) |
To merge this branch: | bzr merge lp:~gmb/launchpad/subscribers-timeout-bug-471974 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | js | Approve | |
Brad Crittenden (community) | code | Approve | |
Review via email: mp+14955@code.launchpad.net |
Commit message
The subscriber-
Description of the change
Graham Binns (gmb) wrote : | # |
Brad Crittenden (bac) wrote : | # |
Hi Graham,
Thanks for this fix. I note there are two issues. One, the icon and subscriber name now appear on different lines and for the life of me I cannot figure out why.
Also I'm bothered that a change of this size has no tests. You could do a view test to check the results of your new view. What about Windmill tests?
Perhaps it is just the current state of affairs but the fragility of this change bothers me.
I've removed the 'js' portion from this review as you may want to get an expert to do that portion.
Graham Binns (gmb) wrote : | # |
On Tue, Nov 17, 2009 at 08:08:18PM -0000, Brad Crittenden wrote:
> Review: Needs Information code
> Hi Graham,
Hi Brad, thanks for the review.
> Thanks for this fix. I note there are two issues. One, the icon and
> subscriber name now appear on different lines and for the life of me I
> cannot figure out why.
That's not a result of this branch; you can see the same behaviour on
edge at the moment. I'll file a bug about it.
>
> Also I'm bothered that a change of this size has no tests. You could
> do a view test to check the results of your new view. What about
> Windmill tests?
>
You're quite right. I've added a view test for the JSON dump (See below
for diff). The changes should still pass the current windmill test
suite, though IIRC the inline subscribers part of the tests is currently
failing anyway, so it's difficult to tell. I'll run it through windmill
before landing.
> Perhaps it is just the current state of affairs but the fragility of
> this change bothers me.
>
I don't think the change is particularly fragile. I designed it
specifically so that if fetching the IDs fails we'll just fall back to
loading the portlet anyway. The only time someone will have a noticeable
problem in that case is if they try to subscribe and unsubscribe quickly
in succession (in which case the subscribe / unsubscribe link may not
update properly) or if they try to unsubscribe from a dupe, in which
case they'll have to do it via the old +subscribe form rather than via
AJAX.
Can you be more specific about the fragility that concerns you?
> I've removed the 'js' portion from this review as you may want to get
> an expert to do that portion.
I'll ask Gavin to take a look since he's OCR today.
Incremental diff of changes
-------
=== added file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -0,0 +1,40 @@
+Bug subscription views
+======
+
+Getting subscriber CSS IDs
+------
+
+It's possible to get a mapping of bug subscriber names to CSS IDs using
+the +bug-portlet-
+
+ >>> from lp.bugs.
+ >>> bug_15 = getUtility(
+
+ >>> subscriber_ids_view = create_
+ ... bug_15, '+bug-portlet-
+
+The view's subscriber_ids_js property returns a JSON struct of the
+person name to CSS ID mappings for the bug's subscribers.
+
+ >>> print subscriber_
+ {"name16": "subscriber-16"}
+
+If bug 15 is marked as a duplicate of another bug, its subscribers will
+be included in that bugs subscriber_ids_js mapping.
+
+ >>> bug_13 = getUtility(
+ >>> subscriber_ids_view = create_
+ ... bug_13, '+bug-portlet-
+
+ >>> print subscriber_
+ {"name12": "subscriber-12"}
+
+ >>> login('<email address hidden>')
+ >>> bug_15.duplicateof = bug_13
+ >>> bug_13 = getUtility(
+
+ >>> subscriber...
Graham Binns (gmb) wrote : | # |
Here's an incremental diff of the changes that Gavin suggested on IRC:
=== modified file 'lib/canonical/
--- lib/canonical/
+++ lib/canonical/
@@ -54,8 +54,7 @@
Y.augment(
Y.bugs.portlet = new PortletTarget();
Y.bugs.
- setup_subscript
- load_subscriber
+ load_subscriber
});
Y.bugs.
setup_
@@ -78,6 +77,22 @@
setup_
});
+/* If loading the subscriber IDs JSON has succeeded, set up the
+ * subscription link handlers and load the subscribers from dupes.
+ */
+Y.bugs.
+ setup_subscript
+ load_subscriber
+});
+
+/* If loading the subscriber IDs JSON fails we still need to load the
+ * subscribers from duplicates but we don't set up the subscription link
+ * handlers.
+ */
+Y.bugs.
+ load_subscriber
+});
+
/*
* Subscribing someone else requires loading a grayed out
* username into the DOM until the subscribe action completes.
@@ -1806,35 +1821,44 @@
}
- function load_portlet() {
- var config = {on: {success: setup_portlet,
- failure: hide_spinner}};
- var url = Y.get(
- '#subscribers-
- 'bugs.', '');
- Y.io(url, config);
- }
+ var config = {on: {success: setup_portlet,
+ failure: hide_spinner}};
+ var url = Y.get(
+ '#subscribers-
+ 'bugs.', '');
+ Y.io(url, config);
+};
- function load_subscriber
+function load_subscriber
+ function on_success(
try {
- var subscriber_ids_node = Y.Node.create(
- response.
- var subscriber_ids_json = subscriber_
- subscriber_ids = Y.JSON.
+ subscriber_ids = Y.JSON.
+ Y.log("Loaded subscriber IDs.");
+
+ // Fire a custom event to trigger the setting-up of the
+ // subscription handlers.
+ Y.bugs.
} catch (e) {
+
+ // Fire an event to signal failure. This ensures that the
+ // subscribers-
+ Y.bugs.
}
-
- load_portlet();
- }
-
- var config = {on: {success: lo...
Brad Crittenden (bac) wrote : | # |
Thanks for adding the view test Graham. I'm satisfied with the code portion and I know Gavin and you have a handle on the JS parts.
Gavin Panella (allenap) : | # |
Preview Diff
1 | === modified file 'lib/canonical/launchpad/javascript/bugs/bugtask-index.js' |
2 | --- lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2009-11-14 21:14:40 +0000 |
3 | +++ lib/canonical/launchpad/javascript/bugs/bugtask-index.js 2009-11-18 15:53:11 +0000 |
4 | @@ -39,6 +39,9 @@ |
5 | var privacy_spinner; |
6 | var link_branch_link; |
7 | |
8 | +// The set of subscriber CSS IDs as a JSON struct. |
9 | +var subscriber_ids; |
10 | + |
11 | /* |
12 | * An object representing the bugtask subscribers portlet. |
13 | * |
14 | @@ -51,8 +54,7 @@ |
15 | Y.augment(PortletTarget, Y.Event.Target); |
16 | Y.bugs.portlet = new PortletTarget(); |
17 | Y.bugs.portlet.subscribe('bugs:portletloaded', function() { |
18 | - setup_subscription_link_handlers(); |
19 | - load_subscribers_from_duplicates(); |
20 | + load_subscriber_ids(); |
21 | }); |
22 | Y.bugs.portlet.subscribe('bugs:dupeportletloaded', function() { |
23 | setup_unsubscribe_icon_handlers(); |
24 | @@ -75,6 +77,22 @@ |
25 | setup_unsubscribe_icon_handlers(); |
26 | }); |
27 | |
28 | +/* If loading the subscriber IDs JSON has succeeded, set up the |
29 | + * subscription link handlers and load the subscribers from dupes. |
30 | + */ |
31 | +Y.bugs.portlet.subscribe('bugs:portletsubscriberidsloaded', function() { |
32 | + setup_subscription_link_handlers(); |
33 | + load_subscribers_from_duplicates(); |
34 | +}); |
35 | + |
36 | +/* If loading the subscriber IDs JSON fails we still need to load the |
37 | + * subscribers from duplicates but we don't set up the subscription link |
38 | + * handlers. |
39 | + */ |
40 | +Y.bugs.portlet.subscribe('bugs:portletsubscriberidsfailed', function() { |
41 | + load_subscribers_from_duplicates(); |
42 | +}); |
43 | + |
44 | /* |
45 | * Subscribing someone else requires loading a grayed out |
46 | * username into the DOM until the subscribe action completes. |
47 | @@ -1792,7 +1810,7 @@ |
48 | } |
49 | } |
50 | |
51 | - function on_success(transactionid, response, args) { |
52 | + function setup_portlet(transactionid, response, args) { |
53 | hide_spinner(); |
54 | var portlet = Y.get('#portlet-subscribers'); |
55 | portlet.set('innerHTML', |
56 | @@ -1803,16 +1821,49 @@ |
57 | Y.bugs.portlet.fire('bugs:portletloaded'); |
58 | } |
59 | |
60 | - var config = {on: {success: on_success, |
61 | + var config = {on: {success: setup_portlet, |
62 | failure: hide_spinner}}; |
63 | var url = Y.get( |
64 | - '#subscribers-content-link').getAttribute('href').replace('bugs.', ''); |
65 | + '#subscribers-content-link').getAttribute('href').replace( |
66 | + 'bugs.', ''); |
67 | Y.io(url, config); |
68 | }; |
69 | |
70 | -}, '0.1', {requires: ['base', 'oop', 'node', 'event', 'io-base', 'substitute', |
71 | - 'widget-position-ext', 'lazr.formoverlay', 'lazr.anim', |
72 | - 'lazr.base', 'lazr.overlay', 'lazr.choiceedit', |
73 | - 'lp.picker', 'lp.client.plugins', 'lp.subscriber', |
74 | +function load_subscriber_ids() { |
75 | + function on_success(transactionid, response, args) { |
76 | + try { |
77 | + subscriber_ids = Y.JSON.parse(response.responseText); |
78 | + Y.log("Loaded subscriber IDs."); |
79 | + |
80 | + // Fire a custom event to trigger the setting-up of the |
81 | + // subscription handlers. |
82 | + Y.bugs.portlet.fire('bugs:portletsubscriberidsloaded'); |
83 | + } catch (e) { |
84 | + Y.log("Unable to load subscriber ids.", "error"); |
85 | + |
86 | + // Fire an event to signal failure. This ensures that the |
87 | + // subscribers-from-dupes still get loaded into the portlet. |
88 | + Y.bugs.portlet.fire('bugs:portletsubscriberidsfailed'); |
89 | + } |
90 | + } |
91 | + |
92 | + function on_failure() { |
93 | + // Fire an event to signal failure. This ensures that the |
94 | + // subscribers-from-dupes still get loaded into the portlet. |
95 | + Y.bugs.portlet.fire('bugs:portletsubscriberidsfailed'); |
96 | + } |
97 | + |
98 | + var config = {on: {success: on_success, |
99 | + failure: on_failure}}; |
100 | + var url = Y.get( |
101 | + '#subscribers-ids-link').getAttribute('href'); |
102 | + Y.io(url, config); |
103 | +} |
104 | + |
105 | +}, '0.1', {requires: ['base', 'oop', 'node', 'event', 'io-base', 'json-parse', |
106 | + 'substitute', 'widget-position-ext', |
107 | + 'lazr.formoverlay', 'lazr.anim', 'lazr.base', |
108 | + 'lazr.overlay', 'lazr.choiceedit', 'lp.picker', |
109 | + 'lp.client.plugins', 'lp.subscriber', |
110 | 'lp.errors']}); |
111 | |
112 | |
113 | === modified file 'lib/lp/bugs/browser/bug.py' |
114 | --- lib/lp/bugs/browser/bug.py 2009-10-30 16:28:41 +0000 |
115 | +++ lib/lp/bugs/browser/bug.py 2009-11-18 15:53:11 +0000 |
116 | @@ -30,7 +30,6 @@ |
117 | import re |
118 | |
119 | import pytz |
120 | -from simplejson import dumps |
121 | |
122 | from zope.app.form.browser import TextWidget |
123 | from zope.component import adapter, getUtility |
124 | @@ -433,11 +432,6 @@ |
125 | ids[sub.name] = 'subscriber-%s' % sub.id |
126 | return ids |
127 | |
128 | - @property |
129 | - def subscriber_ids_js(self): |
130 | - """Return subscriber_ids in a form suitable for JavaScript use.""" |
131 | - return dumps(self.subscriber_ids) |
132 | - |
133 | def subscription_class(self, subscribed_person): |
134 | """Return a set of CSS class names based on subscription status. |
135 | |
136 | |
137 | === modified file 'lib/lp/bugs/browser/bugsubscription.py' |
138 | --- lib/lp/bugs/browser/bugsubscription.py 2009-11-05 19:01:12 +0000 |
139 | +++ lib/lp/bugs/browser/bugsubscription.py 2009-11-18 15:53:11 +0000 |
140 | @@ -10,6 +10,7 @@ |
141 | 'BugSubscriptionAddView', |
142 | ] |
143 | |
144 | +from simplejson import dumps |
145 | from zope.event import notify |
146 | |
147 | from lazr.delegates import delegates |
148 | @@ -97,6 +98,19 @@ |
149 | for subscription in self.context.getSubscriptionsFromDuplicates()] |
150 | |
151 | |
152 | +class BugPortletSubcribersIds(LaunchpadView, BugViewMixin): |
153 | + """A view that returns a JSON dump of the subscriber IDs for a bug.""" |
154 | + |
155 | + @property |
156 | + def subscriber_ids_js(self): |
157 | + """Return subscriber_ids in a form suitable for JavaScript use.""" |
158 | + return dumps(self.subscriber_ids) |
159 | + |
160 | + def render(self): |
161 | + """Override the default render() to return only JSON.""" |
162 | + return self.subscriber_ids_js |
163 | + |
164 | + |
165 | class SubscriptionAttrDecorator: |
166 | """A BugSubscription with added attributes for HTML/JS.""" |
167 | delegates(IBugSubscription, 'subscription') |
168 | |
169 | === modified file 'lib/lp/bugs/browser/configure.zcml' |
170 | --- lib/lp/bugs/browser/configure.zcml 2009-11-10 11:31:06 +0000 |
171 | +++ lib/lp/bugs/browser/configure.zcml 2009-11-18 15:53:11 +0000 |
172 | @@ -1004,6 +1004,11 @@ |
173 | class="lp.bugs.browser.bugsubscription.BugPortletDuplicateSubcribersContents" |
174 | template="../templates/bug-portlet-dupe-subscribers-content.pt" |
175 | permission="zope.Public"/> |
176 | + <browser:page |
177 | + for="lp.bugs.interfaces.bug.IBug" |
178 | + name="+bug-portlet-subscribers-ids" |
179 | + class="lp.bugs.browser.bugsubscription.BugPortletSubcribersIds" |
180 | + permission="zope.Public"/> |
181 | <browser:navigation |
182 | module="lp.bugs.browser.bug" |
183 | classes=" |
184 | |
185 | === added file 'lib/lp/bugs/browser/tests/bug-subscription-views.txt' |
186 | --- lib/lp/bugs/browser/tests/bug-subscription-views.txt 1970-01-01 00:00:00 +0000 |
187 | +++ lib/lp/bugs/browser/tests/bug-subscription-views.txt 2009-11-18 15:53:11 +0000 |
188 | @@ -0,0 +1,40 @@ |
189 | +Bug subscription views |
190 | +====================== |
191 | + |
192 | +Getting subscriber CSS IDs |
193 | +-------------------------- |
194 | + |
195 | +It's possible to get a mapping of bug subscriber names to CSS IDs using |
196 | +the +bug-portlet-subscribers-ids view. |
197 | + |
198 | + >>> from lp.bugs.interfaces.bug import IBugSet |
199 | + >>> bug_15 = getUtility(IBugSet).get(15) |
200 | + |
201 | + >>> subscriber_ids_view = create_initialized_view( |
202 | + ... bug_15, '+bug-portlet-subscribers-ids') |
203 | + |
204 | +The view's subscriber_ids_js property returns a JSON struct of the |
205 | +person name to CSS ID mappings for the bug's subscribers. |
206 | + |
207 | + >>> print subscriber_ids_view.subscriber_ids_js |
208 | + {"name16": "subscriber-16"} |
209 | + |
210 | +If bug 15 is marked as a duplicate of another bug, its subscribers will |
211 | +be included in that bugs subscriber_ids_js mapping. |
212 | + |
213 | + >>> bug_13 = getUtility(IBugSet).get(13) |
214 | + >>> subscriber_ids_view = create_initialized_view( |
215 | + ... bug_13, '+bug-portlet-subscribers-ids') |
216 | + |
217 | + >>> print subscriber_ids_view.subscriber_ids_js |
218 | + {"name12": "subscriber-12"} |
219 | + |
220 | + >>> login('foo.bar@canonical.com') |
221 | + >>> bug_15.duplicateof = bug_13 |
222 | + >>> bug_13 = getUtility(IBugSet).get(13) |
223 | + |
224 | + >>> subscriber_ids_view = create_initialized_view( |
225 | + ... bug_13, '+bug-portlet-subscribers-ids') |
226 | + |
227 | + >>> print subscriber_ids_view.subscriber_ids_js |
228 | + {"name12": "subscriber-12", "name16": "subscriber-16"} |
229 | |
230 | === modified file 'lib/lp/bugs/templates/bug-portlet-subscribers.pt' |
231 | --- lib/lp/bugs/templates/bug-portlet-subscribers.pt 2009-11-05 14:56:01 +0000 |
232 | +++ lib/lp/bugs/templates/bug-portlet-subscribers.pt 2009-11-18 15:53:11 +0000 |
233 | @@ -14,6 +14,9 @@ |
234 | <div id="sub-unsub-spinner">Subscribing...</div> |
235 | <div tal:content="structure context_menu/addsubscriber/render" /> |
236 | </div> |
237 | + <a id="subscribers-ids-link" |
238 | + tal:define="bug context/bug|context" |
239 | + tal:attributes="href bug/fmt:url/+bug-portlet-subscribers-ids"></a> |
240 | <a id="subscribers-content-link" |
241 | tal:define="bug context/bug|context" |
242 | tal:attributes="href bug/fmt:url/+bug-portlet-subscribers-content"></a> |
243 | |
244 | === modified file 'lib/lp/bugs/templates/bugtask-index.pt' |
245 | --- lib/lp/bugs/templates/bugtask-index.pt 2009-10-28 06:08:11 +0000 |
246 | +++ lib/lp/bugs/templates/bugtask-index.pt 2009-11-18 15:53:11 +0000 |
247 | @@ -10,8 +10,6 @@ |
248 | <script type='text/javascript' tal:content="string:var yui_base='${yui}';" /> |
249 | <script type='text/javascript' id='available-official-tags-js' |
250 | tal:content="view/available_official_tags_js" /> |
251 | - <script type="text/javascript" |
252 | - tal:content="string:var subscriber_ids = ${view/subscriber_ids_js};" /> |
253 | <tal:devmode condition="devmode"> |
254 | <script type="text/javascript" |
255 | tal:define="lp_js string:${icingroot}/build" |
This branch fixes bug 471974 by moving the loading of the JSON struct of
subscriber IDs out of the main bug page template. Instead, we'll now
lazily-load it when we actually need it - i.e. when we're loading the
subscribers portlet.
The lazy-loading involves requesting a JSON dump from the server, via a
view designed precisely for that task. This seems a bit hacky, but it's
the quickest way to solve the problem. We will investigate more elegant
solutions later.
The upshot of this is that we shouldn't see any more timeouts on the bug
page due to large numbers of subscribers (especially from dupes).
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files: /launchpad/ javascript/ bugs/bugtask- index.js bugs/browser/ bug.py bugs/browser/ bugsubscription .py bugs/browser/ configure. zcml bugs/templates/ bug-portlet- subscribers- ids.pt bugs/templates/ bug-portlet- subscribers. pt bugs/templates/ bugtask- index.pt
lib/canonical
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
== JSLint notices == graham/ canonical/ lp-branches/ subscribers- timeout-bug-471974/lib/canonical/ launchpad/ javascript/ bugs/bugtask- index.js' .
jslint: No problem found in '/home/
jslint: 1 file to lint.
== Pylint notices ==
lib/lp/ bugs/browser/ bug.py MIMEMultipart' (No module named MIMEMultipart) .event' (No module named lifecycle) .snapshot' (No module named lifecycle) interfaces' (No module named restful)
28: [F0401] Unable to import 'email.
29: [F0401] Unable to import 'email.MIMEText' (No module named MIMEText)
43: [F0401] Unable to import 'lazr.enum' (No module named enum)
44: [F0401] Unable to import 'lazr.lifecycle
45: [F0401] Unable to import 'lazr.lifecycle
46: [F0401] Unable to import 'lazr.restful.
lib/lp/ bugs/browser/ bugsubscription .py .event' (No module named lifecycle)
16: [F0401] Unable to import 'lazr.delegates' (No module named delegates)
17: [F0401] Unable to import 'lazr.lifecycle
= Changes made =
== lib/canonical/ launchpad/ javascript/ bugs/bugtask- index.js ==
- I've added Javascript to load the subscriber_ids struct just before
loading the contents of the subscribers portlet. This should stop the
bug page from timing out.
- Note that, should loading the subscriber ids from the JSON dump time
out (which is theoretically possible), the portlet will still load.
== lib/lp/ bugs/browser/ bug.py ==
- I've removed the subscriber_ids_json property from the BugViewMixin.
It belongs specifically in our JSON-dumping view.
== lib/lp/ bugs/browser/ bugsubscription .py ==
- I've added a BugPortletSubsc ribersIds view. This contains a ids_json property, which returns a JSON dump of the
subscriber_
subscriber IDs for the current bug.
== lib/lp/ bugs/browser/ configure. zcml ==
- I've added the ZCML for the new view.
== lib/lp/ bugs/templates/ bug-portlet- subscribers- ids.pt ==
- I've added this view to contain the JSON dump.
== lib/lp/ bugs/templates/ bug-portlet- subscribers. pt ==
- I've added a link to the bug-portlet- subscribers- ids URL so that we
can grab it using JS and load the JSON dump from it.
== lib...