Merge lp:~abentley/launchpad/translations-sharing into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 13548
Proposed branch: lp:~abentley/launchpad/translations-sharing
Merge into: lp:launchpad
Prerequisite: lp:~abentley/launchpad/reload-cache
Diff against target: 427 lines (+152/-118)
3 files modified
lib/lp/translations/javascript/sourcepackage_sharing_details.js (+49/-91)
lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html (+1/-0)
lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js (+102/-27)
To merge this branch: bzr merge lp:~abentley/launchpad/translations-sharing
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+69531@code.launchpad.net

Commit message

+sharing-details uses load_model.

Description of the change

= Summary =
Reduce roundtrips on +sharing-details page

== Proposed fix ==
Use the new lp.client.load_model functionality to retrieve an updated copy of
the IJSONRequestCache.

== Pre-implementation notes ==
None

== Implementation details ==
This is based on ~abentley/launchpad/reload-cache, which implemented the
load_model functionality.

Most of the updates that happen on +sharing-details require updating more than
one object. In this branch, those methods are changed to use load_model,
via IOHandler.refresh_config.

TranslationSharingController.update_from_model is extracted to update
everything from a new copy of the model. This makes many existing functions
redundant, and so they are deleted.

IOHandler.refresh_config automates the whole process, including the green flash
on success. This is now tested, using IORecorder. The simplification means
that chain_config is no longer needed for most operations; get_config can be
used instead.

== Tests ==
bin/test -t test_sourcepackage_sharing_details

== Demo and Q/A ==
Go to the +sharing-details for a sourcepackage. All operations should succeed,
and do so quickly.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/javascript/tests/test_lp_client.js
  lib/lp/soyuz/browser/distroseriessourcepackagerelease.py
  lib/lp/registry/browser/sourcepackage.py
  lib/lp/registry/browser/peoplemerge.py
  lib/canonical/launchpad/webapp/publisher.py
  lib/canonical/launchpad/webapp/templates/launchpad-model.pt
  lib/lp/code/browser/bazaar.py
  lib/lp/registry/browser/codeofconduct.py
  lib/lp/soyuz/browser/sourcepackage.py
  lib/lp/registry/browser/distributionsourcepackage.py
  lib/canonical/launchpad/webapp/tests/test_publisher.py
  lib/lp/app/javascript/client.js
  lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js
  lib/lp/app/javascript/tests/test_lp_client.html
  lib/lp/soyuz/browser/distroseriesbinarypackage.py
  lib/lp/app/templates/base-layout-macros.pt
  lib/lp/testing/__init__.py
  lib/lp/bugs/browser/configure.zcml
  lib/canonical/launchpad/webapp/tests/test_view_model.py
  lib/lp/bugs/browser/cve.py
  lib/lp/soyuz/browser/distroarchseriesbinarypackagerelease.py
  lib/lp/soyuz/browser/distroarchseriesbinarypackage.py
  lib/lp/translations/browser/tests/test_sharing_details.py
  lib/canonical/launchpad/webapp/configure.zcml
  lib/lp/translations/browser/translationgroup.py
  lib/lp/app/webservice/tests/test_marshallers.py
  lib/lp/blueprints/browser/configure.zcml
  lib/lp/app/javascript/testing/iorecorder.js
  lib/lp/translations/javascript/sourcepackage_sharing_details.js
  lib/lp/registry/browser/teammembership.py
  lib/lp/app/webservice/marshallers.py
  lib/lp/translations/browser/translations.py
  lib/canonical/launchpad/webapp/namespace.py
  lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html
  lib/lp/translations/browser/sourcepackage.py
  lib/lp/registry/browser/tests/test_subscription_links.py

./lib/lp/translations/browser/tests/test_sharing_details.py
     218: E251 no spaces around keyword / parameter equals
     226: E251 no spaces around keyword / parameter equals
     254: E251 no spaces around keyword / parameter equals
     265: E251 no spaces around keyword / parameter equals
     277: E251 no spaces around keyword / parameter equals
     311: E251 no spaces around keyword / parameter equals

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

This looks good.

I'm really impressed by the cleanup of all the methods that update parts of an object into a method that updates everything; this is very cool work.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/translations/javascript/sourcepackage_sharing_details.js'
2--- lib/lp/translations/javascript/sourcepackage_sharing_details.js 2011-07-28 15:32:42 +0000
3+++ lib/lp/translations/javascript/sourcepackage_sharing_details.js 2011-07-28 15:32:44 +0000
4@@ -219,6 +219,8 @@
5
6 function IOHandler(controller, check, error_handler) {
7 that = this;
8+ this.check = check;
9+ this.controller = controller;
10 if (!Y.Lang.isValue(error_handler)){
11 this.error_handler = new Y.lp.client.ErrorHandler();
12 }
13@@ -233,6 +235,23 @@
14 };
15 }
16
17+IOHandler.prototype.show_success = function(){
18+ this.check.set('pending', false);
19+ this.controller.update();
20+ this.controller.flash_check_green(this.check);
21+};
22+
23+IOHandler.prototype.refresh_from_model = function(model){
24+ this.controller.update_from_model(model);
25+ this.show_success();
26+};
27+
28+IOHandler.prototype.refresh_config = function(){
29+ return this.chain_config(
30+ Y.bind("load_model", this.controller),
31+ Y.bind('refresh_from_model', this));
32+};
33+
34
35 /**
36 * Return an LP client config using error_handler.
37@@ -273,6 +292,9 @@
38 };
39
40
41+namespace.IOHandler = IOHandler;
42+
43+
44 /**
45 * This class is the controller for updating the TranslationSharingConfig.
46 * It handles updating the HTML and the DB model.
47@@ -296,17 +318,24 @@
48 * @param branch_summary {Object} An object containing api_url, css,
49 * description, value, title
50 */
51- configure: function(config, branch_picker_config, unlink_overlay,
52+ configure: function(model, branch_picker_config, unlink_overlay,
53 import_overlay, usage_overlay) {
54 this.set('branch_picker_config', branch_picker_config);
55- this.set('source_package', config.context);
56 this.set('unlink_overlay', unlink_overlay);
57 this.set('import_overlay', import_overlay);
58 this.set('usage_overlay', usage_overlay);
59- this.replace_productseries(config.productseries);
60- this.replace_product(config.product);
61- this.set_branch(config.upstream_branch);
62- this.set_permissions(config);
63+ this.update_from_model(model);
64+ },
65+ update_from_model: function(model) {
66+ this.set('source_package', model.context);
67+ this.replace_productseries(model.productseries);
68+ this.replace_product(model.product);
69+ this.set_branch(model.upstream_branch);
70+ this.set_permissions(model);
71+ },
72+ load_model: function(config){
73+ var source_package = this.get('source_package');
74+ Y.lp.client.load_model(source_package, '+sharing-details', config);
75 },
76 set_permissions: function(permissions){
77 var usage = this.get('tsconfig').get('translations_usage');
78@@ -399,37 +428,10 @@
79 var source_package = that.get('source_package');
80 config.parameters = {
81 productseries: productseries_summary.api_uri};
82- source_package.named_post(
83- 'setPackagingReturnSharingDetailPermissions', config);
84- }
85- function get_productseries(config, permissions) {
86- that.set_permissions(permissions);
87- lp_client.get(productseries_summary.api_uri, config);
88- }
89- function cache_productseries(config, productseries) {
90- that.replace_productseries(productseries);
91- var branch_link = productseries.get('branch_link');
92- if (branch_link === null){
93- config.on.success(null);
94- }
95- else {
96- lp_client.get(branch_link, config);
97- }
98- }
99- function cache_branch(config, branch) {
100- that.set_branch(branch);
101- project_link = that.get('productseries').get('project_link');
102- lp_client.get(project_link, config);
103- }
104- function set_usage(product) {
105- that.replace_product(product);
106- productseries_check.set('pending', false);
107- that.update();
108- that.flash_check_green(productseries_check);
109+ source_package.named_post('setPackaging', config);
110 }
111 var io_handler = new IOHandler(this, productseries_check);
112- save_productseries(io_handler.chain_config(
113- get_productseries, cache_productseries, cache_branch, set_usage));
114+ save_productseries(io_handler.refresh_config());
115 },
116 remove_productseries: function(productseries_summary) {
117 var that = this;
118@@ -444,12 +446,10 @@
119 that.replace_productseries(null);
120 that.replace_product(null);
121 that.set_branch(null);
122- productseries_check.set('pending', false);
123- that.update();
124- that.flash_check_green(productseries_check);
125+ io_handler.show_success();
126 }
127 var io_handler = new IOHandler(this, productseries_check);
128- delete_packaging(io_handler.chain_config(set_checks));
129+ delete_packaging(io_handler.get_config(set_checks));
130 },
131 select_branch: function(branch_summary) {
132 var that = this;
133@@ -457,35 +457,14 @@
134 var branch_check = that.get('tsconfig').get('branch');
135 var productseries = that.get('productseries');
136
137- /* Here begin a series of methods which each represent a step in
138- * setting the branch. They each take a config to use in an lp_client
139- * call, except the last one. This allows them to be chained
140- * together.
141- *
142- * They take full advantage of their access to variables in the
143- * closure, such as "that" and "branch_summary".
144- */
145 function save_branch(config) {
146 branch_check.set('pending', true);
147 that.update_check(branch_check);
148 productseries.set('branch_link', branch_summary.api_uri);
149 productseries.lp_save(config);
150 }
151- function get_branch(config){
152- lp_client.get(branch_summary.api_uri, config);
153- }
154- function set_link(config, branch){
155- that.set_branch(branch);
156- lp_client.get(productseries.get('self_link'), config);
157- }
158- function finish(new_productseries){
159- that.replace_productseries(new_productseries);
160- branch_check.set('pending', false);
161- that.update();
162- that.flash_check_green(branch_check);
163- }
164 var io_handler = new IOHandler(this, branch_check);
165- save_branch(io_handler.chain_config(get_branch, set_link, finish));
166+ save_branch(io_handler.refresh_config());
167 },
168 /**
169 * Update the display of all checklist items.
170@@ -510,6 +489,9 @@
171 visible_check_selector: function(check) {
172 return this.check_selector(check, check.get('complete'));
173 },
174+ spinner_selector: function(check) {
175+ return this.visible_check_selector(check) + '-spinner';
176+ },
177 picker_selector: function(check, complete) {
178 return this.check_selector(check, complete) + '-picker a';
179 },
180@@ -541,8 +523,7 @@
181 if (incomplete_picker !== null) {
182 incomplete_picker.toggleClass('unseen', hide_picker);
183 }
184- var selector = this.visible_check_selector(check) + '-spinner';
185- var spinner = Y.one(selector);
186+ var spinner = Y.one(this.spinner_selector(check));
187 if (Y.Lang.isValue(spinner)){
188 spinner.toggleClass('unseen', !check.get('pending'));
189 }
190@@ -611,9 +592,7 @@
191 handler = new IOHandler(sharing_controller, autoimport_check);
192 function update_controller() {
193 sharing_controller.set_autoimport_mode(mode);
194- autoimport_check.set('pending', false);
195- sharing_controller.update();
196- sharing_controller.flash_check_green(autoimport_check);
197+ handler.show_success();
198 }
199 autoimport_check.set('pending', true);
200 sharing_controller.update_check(autoimport_check);
201@@ -624,7 +603,7 @@
202 * the value stored in productseries.
203 */
204 product_series.removeAttr('http_etag');
205- product_series.lp_save(handler.chain_config(update_controller));
206+ product_series.lp_save(handler.get_config(update_controller));
207 });
208 var autoimport = sharing_controller.get('tsconfig').get('autoimport');
209 sharing_controller.set_check_picker(autoimport, import_overlay);
210@@ -632,33 +611,11 @@
211 var usage_overlay = create_form_overlay(
212 '<h2>Configure translations<h2>', function(form_data) {
213 var product = sharing_controller.get('product');
214- function reload_entry(config, entry) {
215- lp_client.get(entry.get('self_link'), config);
216- }
217- function get_productseries(config) {
218- var productseries = sharing_controller.get('productseries');
219- reload_entry(config, productseries);
220- }
221- function replace_productseries(config, new_productseries) {
222- sharing_controller.replace_productseries(new_productseries);
223- config.on.success();
224- }
225- function get_product(config) {
226- reload_entry(config, product);
227- }
228- function replace_product(new_product) {
229- sharing_controller.replace_product(new_product);
230- usage.set('pending', false);
231- sharing_controller.update();
232- sharing_controller.flash_check_green(usage);
233- }
234 var io_handler = new IOHandler(
235 sharing_controller, usage, new Y.lp.client.FormErrorHandler());
236 usage.set('pending', true);
237 sharing_controller.update_check(usage);
238- var config = io_handler.chain_config(
239- get_productseries, replace_productseries, get_product,
240- replace_product);
241+ var config = io_handler.refresh_config();
242 submit_form(
243 config, form_data, product, '+configure-translations', 'change');
244 });
245@@ -669,4 +626,5 @@
246 sharing_controller.update();
247 };
248 }, "0.1", {"requires": [
249- 'lp', 'lp.app.errors', 'lp.app.picker', 'oop', 'lp.client']});
250+ 'lp', 'lp.app.errors', 'lp.app.picker', 'oop', 'lp.client',
251+ 'lazr.anim']});
252
253=== modified file 'lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html'
254--- lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html 2011-07-08 06:06:15 +0000
255+++ lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.html 2011-07-28 15:32:44 +0000
256@@ -12,6 +12,7 @@
257
258 <script type="text/javascript" src="../../../app/javascript/client.js"></script>
259 <script type="text/javascript" src="../../../app/javascript/lp.js"></script>
260+ <script type="text/javascript" src="../../../app/javascript/anim/anim.js"></script>
261
262 <!-- The module under test -->
263 <script type="text/javascript" src="../sourcepackage_sharing_details.js"></script>
264
265=== modified file 'lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js'
266--- lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js 2011-07-28 15:32:42 +0000
267+++ lib/lp/translations/javascript/tests/test_sourcepackage_sharing_details.js 2011-07-28 15:32:44 +0000
268@@ -7,6 +7,7 @@
269
270 var suite = new Y.Test.Suite("sourcepackage_sharing_details Tests");
271 var namespace = Y.lp.translations.sourcepackage_sharing_details;
272+ var IOHandler = namespace.IOHandler;
273 var TranslationSharingConfig = namespace.TranslationSharingConfig;
274 var TranslationSharingController = namespace.TranslationSharingController;
275 var CheckItem = (
276@@ -101,12 +102,12 @@
277 },
278 test_configure_empty: function() {
279 var ctrl = new TranslationSharingController();
280- var cache = {
281+ var model = {
282 productseries: null,
283 product: null,
284 upstream_branch: null
285 };
286- ctrl.configure(cache, {});
287+ ctrl.configure(model, {});
288 },
289 test_configure: function() {
290 var cache = {
291@@ -133,31 +134,90 @@
292 };
293 var ctrl = new TranslationSharingController();
294 var lp_client = new Y.lp.client.Launchpad();
295- cache = lp_client.wrap_resource(null, cache);
296- var import_overlay = {
297- loadFormContentAndRender: function() {},
298- render: function() {},
299- get: function(ignore) {
300- return Y.Node.create(
301- '<p><a href="http://fake">fake</a></p>');
302- }
303- };
304- unlink_overlay = import_overlay;
305- usage_overlay = import_overlay;
306- ctrl.configure(
307- cache, {}, unlink_overlay, import_overlay, usage_overlay);
308- var tsconfig = ctrl.get('tsconfig');
309- Y.Assert.areEqual(
310- tsconfig.get('product_series').get('text'), 'title1');
311- Y.Assert.areEqual(
312- tsconfig.get('product_series').get('url'), 'http://web1');
313- Y.Assert.areEqual(
314- tsconfig.get('branch').get('text'), 'lp:title2');
315- Y.Assert.isTrue(tsconfig.get('branch').get('user_authorized'));
316- Y.Assert.isTrue(tsconfig.get('autoimport').get('complete'));
317- Y.Assert.isTrue(
318- tsconfig.get('translations_usage').get('complete'));
319- Y.Assert.areSame(ctrl.get('source_package'), cache.context);
320+ var model = lp_client.wrap_resource(null, cache);
321+ var import_overlay = {
322+ loadFormContentAndRender: function() {},
323+ render: function() {},
324+ get: function(ignore) {
325+ return Y.Node.create(
326+ '<p><a href="http://fake">fake</a></p>');
327+ }
328+ };
329+ unlink_overlay = import_overlay;
330+ usage_overlay = import_overlay;
331+ ctrl.configure(
332+ model, {}, unlink_overlay, import_overlay, usage_overlay);
333+ var tsconfig = ctrl.get('tsconfig');
334+ Y.Assert.areEqual(
335+ tsconfig.get('product_series').get('text'), 'title1');
336+ Y.Assert.areEqual(
337+ tsconfig.get('product_series').get('url'), 'http://web1');
338+ Y.Assert.areEqual(
339+ tsconfig.get('branch').get('text'), 'lp:title2');
340+ Y.Assert.isTrue(tsconfig.get('branch').get('user_authorized'));
341+ Y.Assert.isTrue(tsconfig.get('autoimport').get('complete'));
342+ Y.Assert.isTrue(
343+ tsconfig.get('translations_usage').get('complete'));
344+ Y.Assert.areSame(ctrl.get('source_package'), model.context);
345+ },
346+ test_update_from_model: function() {
347+ var null_model = {
348+ productseries: null,
349+ product: null,
350+ upstream_branch: null
351+ };
352+
353+ var cache = {
354+ product: {
355+ translations_usage: test_ns.usage.launchpad,
356+ resource_type_link: 'http://product'
357+ },
358+ productseries: {
359+ title: 'title1',
360+ web_link: 'http://web1',
361+ translations_autoimport_mode: (
362+ test_ns.autoimport_modes.import_translations),
363+ resource_type_link: 'productseries'
364+ },
365+ upstream_branch: {
366+ unique_name: 'title2',
367+ web_link: 'http://web2',
368+ resource_type_link: 'branch'
369+ },
370+ context: {
371+ resource_type_link: 'http://sourcepackage'
372+ },
373+ user_can_change_branch: true
374+ };
375+ var ctrl = new TranslationSharingController();
376+ var lp_client = new Y.lp.client.Launchpad();
377+ var model = lp_client.wrap_resource(null, cache);
378+ var import_overlay = {
379+ loadFormContentAndRender: function() {},
380+ render: function() {},
381+ get: function(ignore) {
382+ return Y.Node.create(
383+ '<p><a href="http://fake">fake</a></p>');
384+ }
385+ };
386+ unlink_overlay = import_overlay;
387+ usage_overlay = import_overlay;
388+ ctrl.configure(
389+ null_model, {}, unlink_overlay, import_overlay,
390+ usage_overlay);
391+ ctrl.update_from_model(model);
392+ var tsconfig = ctrl.get('tsconfig');
393+ Y.Assert.areEqual(
394+ tsconfig.get('product_series').get('text'), 'title1');
395+ Y.Assert.areEqual(
396+ tsconfig.get('product_series').get('url'), 'http://web1');
397+ Y.Assert.areEqual(
398+ tsconfig.get('branch').get('text'), 'lp:title2');
399+ Y.Assert.isTrue(tsconfig.get('branch').get('user_authorized'));
400+ Y.Assert.isTrue(tsconfig.get('autoimport').get('complete'));
401+ Y.Assert.isTrue(
402+ tsconfig.get('translations_usage').get('complete'));
403+ Y.Assert.areSame(ctrl.get('source_package'), model.context);
404 },
405 test_set_permissions: function(){
406 var ctrl = new TranslationSharingController();
407@@ -324,6 +384,21 @@
408 Y.Assert.isTrue(check.get('complete'));
409 }
410 }));
411+ suite.add(new Y.Test.Case({
412+ name: 'setup',
413+ test_show_success: function(){
414+ var controller = new TranslationSharingController();
415+ var check = controller.get('tsconfig').get('branch');
416+ check.set('pending', true);
417+ controller.update_check(check);
418+ spinner = Y.one(controller.spinner_selector(check));
419+ Y.Assert.isFalse(spinner.hasClass('unseen'));
420+ io_handler = new IOHandler(controller, check);
421+ io_handler.show_success();
422+ Y.Assert.isFalse(check.get('pending'));
423+ Y.Assert.isTrue(spinner.hasClass('unseen'));
424+ }
425+ }));
426
427 Y.lp.testing.Runner.run(suite);
428