Merge lp:~blake-rouse/maas/fix-ui-cache into lp:~maas-committers/maas/trunk
- fix-ui-cache
- Merge into trunk
Proposed by
Blake Rouse
Status: | Merged |
---|---|
Approved by: | Blake Rouse |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4492 |
Proposed branch: | lp:~blake-rouse/maas/fix-ui-cache |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
442 lines (+172/-128) 8 files modified
src/maas/settings.py (+0/-1) src/maasserver/context_processors.py (+3/-75) src/maasserver/middleware.py (+4/-3) src/maasserver/templates/maasserver/css-conf.html (+2/-3) src/maasserver/templates/maasserver/js-conf.html (+12/-12) src/maasserver/urls_combo.py (+5/-12) src/maasserver/views/combo.py (+121/-0) src/maasserver/views/tests/test_combo.py (+25/-22) |
To merge this branch: | bzr merge lp:~blake-rouse/maas/fix-ui-cache |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andres Rodriguez (community) | Approve | ||
Review via email: mp+277365@code.launchpad.net |
Commit message
Fix loading of css and js to bust the cache on upgrade.
Description of the change
To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote : | # |
Revision history for this message
Andres Rodriguez (andreserl) wrote : | # |
Haven't tested it, but looks good to me!
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/maas/settings.py' |
2 | --- src/maas/settings.py 2015-10-14 18:32:40 +0000 |
3 | +++ src/maas/settings.py 2015-11-12 16:50:21 +0000 |
4 | @@ -180,7 +180,6 @@ |
5 | "django.contrib.messages.context_processors.messages", |
6 | "maasserver.context_processors.yui", |
7 | "maasserver.context_processors.global_options", |
8 | - "maasserver.context_processors.static_resources", |
9 | ) |
10 | |
11 | MIDDLEWARE_CLASSES = ( |
12 | |
13 | === modified file 'src/maasserver/context_processors.py' |
14 | --- src/maasserver/context_processors.py 2015-11-09 15:09:26 +0000 |
15 | +++ src/maasserver/context_processors.py 2015-11-12 16:50:21 +0000 |
16 | @@ -14,7 +14,6 @@ |
17 | __metaclass__ = type |
18 | __all__ = [ |
19 | "global_options", |
20 | - "static_resources", |
21 | "yui", |
22 | ] |
23 | |
24 | @@ -33,86 +32,15 @@ |
25 | } |
26 | |
27 | |
28 | -def static_resources(context): |
29 | - return { |
30 | - 'CSS_LIST': [ |
31 | - 'css/base.css', |
32 | - 'css/maas-styles.css', |
33 | - ], |
34 | - 'ANGULAR_LIST': [ |
35 | - 'js/angular/maas.js', |
36 | - 'js/angular/factories/region.js', |
37 | - 'js/angular/factories/nodes.js', |
38 | - 'js/angular/factories/devices.js', |
39 | - 'js/angular/factories/clusters.js', |
40 | - 'js/angular/factories/zones.js', |
41 | - 'js/angular/factories/general.js', |
42 | - 'js/angular/factories/users.js', |
43 | - 'js/angular/factories/events.js', |
44 | - 'js/angular/factories/tags.js', |
45 | - 'js/angular/factories/subnets.js', |
46 | - 'js/angular/factories/spaces.js', |
47 | - 'js/angular/factories/vlans.js', |
48 | - 'js/angular/factories/fabrics.js', |
49 | - 'js/angular/services/search.js', |
50 | - 'js/angular/services/manager.js', |
51 | - 'js/angular/services/managerhelper.js', |
52 | - 'js/angular/services/error.js', |
53 | - 'js/angular/services/validation.js', |
54 | - 'js/angular/services/browser.js', |
55 | - 'js/angular/services/converter.js', |
56 | - 'js/angular/services/json.js', |
57 | - 'js/angular/directives/error_overlay.js', |
58 | - 'js/angular/directives/code_lines.js', |
59 | - 'js/angular/directives/error_toggle.js', |
60 | - 'js/angular/directives/call_to_action.js', |
61 | - 'js/angular/directives/power_parameters.js', |
62 | - 'js/angular/directives/os_select.js', |
63 | - 'js/angular/directives/type.js', |
64 | - 'js/angular/directives/accordion.js', |
65 | - 'js/angular/directives/dbl_click_overlay.js', |
66 | - 'js/angular/directives/contenteditable.js', |
67 | - 'js/angular/directives/sticky_header.js', |
68 | - 'js/angular/directives/placeholder.js', |
69 | - 'js/angular/directives/enter_blur.js', |
70 | - 'js/angular/directives/version_reloader.js', |
71 | - 'js/angular/filters/nodes.js', |
72 | - 'js/angular/filters/by_fabric.js', |
73 | - 'js/angular/filters/by_vlan.js', |
74 | - 'js/angular/filters/by_space.js', |
75 | - 'js/angular/filters/remove_default_vlan.js', |
76 | - 'js/angular/controllers/error.js', |
77 | - 'js/angular/controllers/nodes_list.js', |
78 | - 'js/angular/controllers/add_hardware.js', |
79 | - 'js/angular/controllers/add_device.js', |
80 | - 'js/angular/controllers/node_details.js', |
81 | - 'js/angular/controllers/node_details_networking.js', |
82 | - 'js/angular/controllers/node_details_storage.js', |
83 | - 'js/angular/controllers/node_result.js', |
84 | - 'js/angular/controllers/node_events.js', |
85 | - 'js/angular/controllers/subnets_list.js', |
86 | - 'js/angular/controllers/subnet_details.js', |
87 | - ], |
88 | - 'JS_LIST': [ |
89 | - 'js/image.js', |
90 | - 'js/image_views.js', |
91 | - 'js/user_panel.js', |
92 | - 'js/prefs.js', |
93 | - 'js/shortpoll.js', |
94 | - 'js/enums.js', |
95 | - 'js/reveal.js', |
96 | - 'js/os_distro_select.js', |
97 | - ], |
98 | - } |
99 | - |
100 | - |
101 | def global_options(context): |
102 | + version = get_maas_version_ui() |
103 | return { |
104 | 'persistent_errors': get_persistent_errors(), |
105 | 'global_options': { |
106 | 'site_name': Config.objects.get_config('maas_name'), |
107 | }, |
108 | 'debug': settings.DEBUG, |
109 | - 'version': get_maas_version_ui(), |
110 | + 'version': version, |
111 | + 'files_version': version.replace(" ", ""), |
112 | 'doc_version': get_maas_doc_version(), |
113 | } |
114 | |
115 | === modified file 'src/maasserver/middleware.py' |
116 | --- src/maasserver/middleware.py 2015-10-19 04:54:14 +0000 |
117 | +++ src/maasserver/middleware.py 2015-11-12 16:50:21 +0000 |
118 | @@ -59,6 +59,7 @@ |
119 | from maasserver.models.nodegroup import NodeGroup |
120 | from maasserver.rpc import getAllClients |
121 | from maasserver.utils.orm import is_serialization_failure |
122 | +from maasserver.views.combo import MERGE_VIEWS |
123 | from provisioningserver.rpc.exceptions import ( |
124 | NoConnectionsAvailable, |
125 | PowerActionAlreadyInProgress, |
126 | @@ -96,9 +97,6 @@ |
127 | reverse('login'), |
128 | # The combo loaders are publicly accessible. |
129 | reverse('combo-yui'), |
130 | - reverse('combo-maas'), |
131 | - reverse('combo-jquery'), |
132 | - reverse('combo-angularjs'), |
133 | # Static resources are publicly visible. |
134 | settings.STATIC_URL_PATTERN, |
135 | reverse('robots'), |
136 | @@ -112,6 +110,9 @@ |
137 | # API calls are protected by piston. |
138 | settings.API_URL_REGEXP, |
139 | ] |
140 | + # Add the defined combo loaders. |
141 | + for filename in MERGE_VIEWS.keys(): |
142 | + public_url_roots.append(reverse('merge', args=[filename])) |
143 | self.public_urls = re.compile("|".join(public_url_roots)) |
144 | self.login_url = reverse('login') |
145 | |
146 | |
147 | === modified file 'src/maasserver/templates/maasserver/css-conf.html' |
148 | --- src/maasserver/templates/maasserver/css-conf.html 2015-02-20 17:06:18 +0000 |
149 | +++ src/maasserver/templates/maasserver/css-conf.html 2015-11-12 16:50:21 +0000 |
150 | @@ -1,3 +1,2 @@ |
151 | -{% for css_file in CSS_LIST %} |
152 | - <link rel="stylesheet" href="{{ STATIC_URL }}{{ css_file }}" /> |
153 | -{% endfor %} |
154 | +<link rel="stylesheet" href="{{ STATIC_URL }}css/base.css?v={{files_version}}" /> |
155 | +<link rel="stylesheet" href="{{ STATIC_URL }}css/maas-styles.css?v={{files_version}}" /> |
156 | |
157 | === modified file 'src/maasserver/templates/maasserver/js-conf.html' |
158 | --- src/maasserver/templates/maasserver/js-conf.html 2015-05-27 14:35:56 +0000 |
159 | +++ src/maasserver/templates/maasserver/js-conf.html 2015-11-12 16:50:21 +0000 |
160 | @@ -1,14 +1,14 @@ |
161 | <script type="text/javascript" |
162 | - src="{% url "combo-jquery" %}?jquery.min.js"> |
163 | -</script> |
164 | -<script type="text/javascript" |
165 | - src="{% url "combo-angularjs" %}?angular.min.js&angular-route.min.js&angular-cookies.min.js"> |
166 | -</script> |
167 | -<script type="text/javascript" |
168 | - src="{% url "combo-maas" %}?js/angular/3rdparty/ng-tags-input.js"> |
169 | -</script> |
170 | -<script type="text/javascript" |
171 | - src="{% url "combo-maas" %}?{{ ANGULAR_LIST|join:"&" }}"> |
172 | + src="{% url "merge" filename="jquery.js" %}?v={{files_version}}"> |
173 | +</script> |
174 | +<script type="text/javascript" |
175 | + src="{% url "merge" filename="angular.js" %}?v={{files_version}}"> |
176 | +</script> |
177 | +<script type="text/javascript" |
178 | + src="{% url "merge" filename="ng-tags-input.js" %}?v={{files_version}}"> |
179 | +</script> |
180 | +<script type="text/javascript" |
181 | + src="{% url "merge" filename="maas-angular.js" %}?v={{files_version}}"> |
182 | </script> |
183 | |
184 | <script type="text/javascript"> |
185 | @@ -35,8 +35,8 @@ |
186 | // --> |
187 | </script> |
188 | <script type="text/javascript" |
189 | - src="{% url "combo-yui" %}?yui-base/yui-base-min.js"> |
190 | + src="{% url "merge" filename="yui.js" %}?v={{files_version}}"> |
191 | </script> |
192 | <script type="text/javascript" |
193 | - src="{% url "combo-maas" %}?{{ JS_LIST|join:"&" }}"> |
194 | + src="{% url "merge" filename="maas-yui.js" %}?v={{files_version}}"> |
195 | </script> |
196 | |
197 | === modified file 'src/maasserver/urls_combo.py' |
198 | --- src/maasserver/urls_combo.py 2015-05-07 18:14:38 +0000 |
199 | +++ src/maasserver/urls_combo.py 2015-11-12 16:50:21 +0000 |
200 | @@ -20,25 +20,18 @@ |
201 | patterns, |
202 | url, |
203 | ) |
204 | -from maasserver.views.combo import get_combo_view |
205 | +from maasserver.views.combo import ( |
206 | + get_combo_view, |
207 | + merge_view, |
208 | +) |
209 | |
210 | |
211 | urlpatterns = patterns( |
212 | '', |
213 | url( |
214 | - r'^maas/', get_combo_view( |
215 | - location='', default_redirect=settings.STATIC_URL), |
216 | - name='combo-maas'), |
217 | - url( |
218 | r'^yui/', |
219 | get_combo_view(location=settings.YUI_LOCATION), |
220 | name='combo-yui'), |
221 | url( |
222 | - r'^jquery/', |
223 | - get_combo_view(location=settings.JQUERY_LOCATION), |
224 | - name='combo-jquery'), |
225 | - url( |
226 | - r'^angularjs/', |
227 | - get_combo_view(location=settings.ANGULARJS_LOCATION), |
228 | - name='combo-angularjs'), |
229 | + r'^(?P<filename>[^/]*)', merge_view, name='merge'), |
230 | ) |
231 | |
232 | === modified file 'src/maasserver/views/combo.py' |
233 | --- src/maasserver/views/combo.py 2015-06-10 10:08:45 +0000 |
234 | +++ src/maasserver/views/combo.py 2015-11-12 16:50:21 +0000 |
235 | @@ -23,6 +23,7 @@ |
236 | combine_files, |
237 | parse_qs, |
238 | ) |
239 | +from django.conf import settings |
240 | from django.http import ( |
241 | HttpResponse, |
242 | HttpResponseBadRequest, |
243 | @@ -32,6 +33,108 @@ |
244 | from maasserver.config import RegionConfiguration |
245 | |
246 | |
247 | +MERGE_VIEWS = { |
248 | + "jquery.js": { |
249 | + "location": settings.JQUERY_LOCATION, |
250 | + "content_type": "text/javascript; charset=UTF-8", |
251 | + "files": [ |
252 | + "jquery.min.js", |
253 | + ] |
254 | + }, |
255 | + "angular.js": { |
256 | + "location": settings.ANGULARJS_LOCATION, |
257 | + "content_type": "text/javascript; charset=UTF-8", |
258 | + "files": [ |
259 | + "angular.min.js", |
260 | + "angular-route.min.js", |
261 | + "angular-cookies.min.js", |
262 | + ] |
263 | + }, |
264 | + "ng-tags-input.js": { |
265 | + "content_type": "text/javascript; charset=UTF-8", |
266 | + "files": [ |
267 | + "js/angular/3rdparty/ng-tags-input.js", |
268 | + ] |
269 | + }, |
270 | + "maas-angular.js": { |
271 | + "content_type": "text/javascript; charset=UTF-8", |
272 | + "files": [ |
273 | + "js/angular/maas.js", |
274 | + "js/angular/factories/region.js", |
275 | + "js/angular/factories/nodes.js", |
276 | + "js/angular/factories/devices.js", |
277 | + "js/angular/factories/clusters.js", |
278 | + "js/angular/factories/zones.js", |
279 | + "js/angular/factories/general.js", |
280 | + "js/angular/factories/users.js", |
281 | + "js/angular/factories/events.js", |
282 | + "js/angular/factories/tags.js", |
283 | + "js/angular/factories/subnets.js", |
284 | + "js/angular/factories/spaces.js", |
285 | + "js/angular/factories/vlans.js", |
286 | + "js/angular/factories/fabrics.js", |
287 | + "js/angular/services/search.js", |
288 | + "js/angular/services/manager.js", |
289 | + "js/angular/services/managerhelper.js", |
290 | + "js/angular/services/error.js", |
291 | + "js/angular/services/validation.js", |
292 | + "js/angular/services/browser.js", |
293 | + "js/angular/services/converter.js", |
294 | + "js/angular/services/json.js", |
295 | + "js/angular/directives/error_overlay.js", |
296 | + "js/angular/directives/code_lines.js", |
297 | + "js/angular/directives/error_toggle.js", |
298 | + "js/angular/directives/call_to_action.js", |
299 | + "js/angular/directives/power_parameters.js", |
300 | + "js/angular/directives/os_select.js", |
301 | + "js/angular/directives/type.js", |
302 | + "js/angular/directives/accordion.js", |
303 | + "js/angular/directives/dbl_click_overlay.js", |
304 | + "js/angular/directives/contenteditable.js", |
305 | + "js/angular/directives/sticky_header.js", |
306 | + "js/angular/directives/placeholder.js", |
307 | + "js/angular/directives/enter_blur.js", |
308 | + "js/angular/directives/version_reloader.js", |
309 | + "js/angular/filters/nodes.js", |
310 | + "js/angular/filters/by_fabric.js", |
311 | + "js/angular/filters/by_vlan.js", |
312 | + "js/angular/filters/by_space.js", |
313 | + "js/angular/filters/remove_default_vlan.js", |
314 | + "js/angular/controllers/nodes_list.js", |
315 | + "js/angular/controllers/add_hardware.js", |
316 | + "js/angular/controllers/add_device.js", |
317 | + "js/angular/controllers/node_details.js", |
318 | + "js/angular/controllers/node_details_networking.js", |
319 | + "js/angular/controllers/node_details_storage.js", |
320 | + "js/angular/controllers/node_result.js", |
321 | + "js/angular/controllers/node_events.js", |
322 | + "js/angular/controllers/subnets_list.js", |
323 | + "js/angular/controllers/subnet_details.js", |
324 | + ] |
325 | + }, |
326 | + "yui.js": { |
327 | + "location": settings.YUI_LOCATION, |
328 | + "content_type": "text/javascript; charset=UTF-8", |
329 | + "files": [ |
330 | + "yui-base/yui-base-min.js", |
331 | + ] |
332 | + }, |
333 | + "maas-yui.js": { |
334 | + "content_type": "text/javascript; charset=UTF-8", |
335 | + "files": [ |
336 | + "js/image.js", |
337 | + "js/image_views.js", |
338 | + "js/user_panel.js", |
339 | + "js/prefs.js", |
340 | + "js/shortpoll.js", |
341 | + "js/enums.js", |
342 | + "js/reveal.js", |
343 | + "js/os_distro_select.js", |
344 | + ] |
345 | + }, |
346 | +} |
347 | + |
348 | + |
349 | def get_absolute_location(location=''): |
350 | """Return the absolute location of a static resource. |
351 | |
352 | @@ -98,3 +201,21 @@ |
353 | content_type=content_type, status=200, content=content) |
354 | |
355 | return HttpResponseNotFound() |
356 | + |
357 | + |
358 | +def merge_view(request, filename): |
359 | + """Merge the `files` from `location` into one file. Return the HTTP |
360 | + response with `content_type`. |
361 | + """ |
362 | + merge_info = MERGE_VIEWS.get(filename, None) |
363 | + if merge_info is None: |
364 | + return HttpResponseNotFound() |
365 | + location = merge_info.get("location", None) |
366 | + if location is None: |
367 | + location = get_absolute_location() |
368 | + content = "".join( |
369 | + [content.decode('utf-8') for content in combine_files( |
370 | + merge_info["files"], location, |
371 | + resource_prefix='/', rewrite_urls=True)]) |
372 | + return HttpResponse( |
373 | + content_type=merge_info["content_type"], status=200, content=content) |
374 | |
375 | === modified file 'src/maasserver/views/tests/test_combo.py' |
376 | --- src/maasserver/views/tests/test_combo.py 2015-06-10 10:08:45 +0000 |
377 | +++ src/maasserver/views/tests/test_combo.py 2015-11-12 16:50:21 +0000 |
378 | @@ -18,7 +18,6 @@ |
379 | import httplib |
380 | import os |
381 | |
382 | -from django.conf import settings |
383 | from django.core.urlresolvers import reverse |
384 | from django.test.client import RequestFactory |
385 | from maasserver import config |
386 | @@ -29,6 +28,7 @@ |
387 | from maasserver.views.combo import ( |
388 | get_absolute_location, |
389 | get_combo_view, |
390 | + MERGE_VIEWS, |
391 | ) |
392 | from maastesting.fixtures import ImportErrorFixture |
393 | |
394 | @@ -152,24 +152,27 @@ |
395 | (httplib.BAD_REQUEST, "Invalid file type requested."), |
396 | (response.status_code, response.content)) |
397 | |
398 | - def test_maas_load_js(self): |
399 | - requested_files = ['js/user_panel.js', 'js/enums.js'] |
400 | - url = '%s?%s' % (reverse('combo-maas'), '&'.join(requested_files)) |
401 | - response = self.client.get(url) |
402 | - # No sign of a missing js file. |
403 | - self.assertNotIn(CONVOY_MISSING_FILE, response.content) |
404 | - |
405 | - def test_maas_load_css(self): |
406 | - requested_files = ['css/base.css'] |
407 | - url = '%s?%s' % (reverse('combo-maas'), '&'.join(requested_files)) |
408 | - response = self.client.get(url) |
409 | - # No sign of a missing css file. |
410 | - self.assertNotIn(CONVOY_MISSING_FILE, response.content) |
411 | - |
412 | - def test_maas_load_image(self): |
413 | - img_path = 'img/bg_dots.png' |
414 | - url = '%s?%s' % (reverse('combo-maas'), img_path) |
415 | - response = self.client.get(url) |
416 | - self.assertEqual( |
417 | - '%s%s' % (settings.STATIC_URL, img_path), |
418 | - extract_redirect(response)) |
419 | + |
420 | +class TestMergeLoaderView(MAASServerTestCase): |
421 | + """Test merge loader views.""" |
422 | + |
423 | + def test_loads_all_views_correctly(self): |
424 | + for filename, merge_info in MERGE_VIEWS.items(): |
425 | + url = reverse('merge', args=[filename]) |
426 | + response = self.client.get(url) |
427 | + self.assertEquals( |
428 | + merge_info["content_type"], response['Content-Type'], |
429 | + "Content-type for %s does not match." % filename) |
430 | + |
431 | + # Has all required files. |
432 | + for requested_file in merge_info["files"]: |
433 | + self.assertIn( |
434 | + requested_file, response.content.decode("utf-8")) |
435 | + |
436 | + # No sign of a missing js file. |
437 | + self.assertNotIn( |
438 | + CONVOY_MISSING_FILE, response.content.decode("utf-8")) |
439 | + |
440 | + def test_load_unknown_returns_302_blocked_by_middleware(self): |
441 | + response = self.client.get(reverse('merge', args=["unknown.js"])) |
442 | + self.assertEqual(httplib.FOUND, response.status_code) |
This forces the browser to reload the file when the installed version is updated. Again this still requires the user to reload the page. But a previous branch has landed that does an auto-reload, but again the client needs to reload to get that updated version to perform the auto reload.