From bcd8283181b929c276d07dad2717213fa1bd3a74 Mon Sep 17 00:00:00 2001 From: Romain Courteaud <romain@nexedi.com> Date: Mon, 19 Aug 2024 13:12:31 +0000 Subject: [PATCH] erp5_hal_json_style/erp5_web: fix http cache condition The main query to the hateoas web section must be cache by the browser, to reduce the number of queries by 2. Caching policy manager conditions are checked before calculating the query result, and so, it is not possible anymore to wait for the script to explicitely ask for the result to be cached. --- .../ERP5Document_getHateoas.py | 1 - .../test.erp5.testHalJsonStyle.py | 111 ++++++++++++++++++ .../caching_policy_manager.xml | 2 +- 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py index 64297d0adb..1d8d885d25 100644 --- a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py +++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py @@ -1615,7 +1615,6 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, if is_site_root: result_dict['default_view'] = 'view' - REQUEST.set("X-HATEOAS-CACHE", 1) # Global action users for the jIO plugin # XXX Would be better to not hardcode them but put them as portal type diff --git a/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py b/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py index 24eda5620c..0b3867bf16 100644 --- a/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py +++ b/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py @@ -15,6 +15,7 @@ import DateTime import StringIO import json import re +import io from six.moves.urllib.parse import quote, quote_plus import mock @@ -3265,3 +3266,113 @@ class TestERP5ODS(ERP5HALJSONStyleSkinsMixin): self.assertTrue('Read-Only Quantity' in result, result) # Ensure it is not the list mode rendering self.assertTrue(len(result.split('\n')) > 50, result) + + +class TestERP5Document_getHateoas_cache(ERP5HALJSONStyleSkinsMixin): + + def testCache_root_authenticated(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/', + user='ERP5TypeTestCase' + ) + self.assertEqual(ret.getStatus(), 200) + self.assertEqual(ret.getHeader('content-type'), 'application/hal+json') + self.assertEqual(ret.getHeader('cache-control'), 'max-age=1800, private') + + def testCache_root_authenticatedWrongPath(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath(), + user='ERP5TypeTestCase' + ) + self.assertEqual(ret.getStatus(), 200) + self.assertEqual(ret.getHeader('content-type'), 'application/hal+json') + self.assertEqual(ret.getHeader('cache-control'), 'max-age=1800, private') + + def testCache_root_authenticatedAndMethodCall(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/getId', + user='ERP5TypeTestCase' + ) + self.assertEqual(ret.getStatus(), 200) + self.assertEqual(ret.getHeader('content-type'), 'text/plain; charset=utf-8') + self.assertEqual(ret.getHeader('cache-control'), 'private') + + def testCache_root_authenticatedWrongQueryString(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/?foo=bar', + user='ERP5TypeTestCase' + ) + self.assertEqual(ret.getStatus(), 200) + self.assertEqual(ret.getHeader('content-type'), 'application/hal+json') + self.assertEqual(ret.getHeader('cache-control'), 'max-age=0, no-cache, private') + + def testCache_root_authenticatedWrongMethod(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/', + user='ERP5TypeTestCase', + request_method='POST' + ) + self.assertEqual(ret.getStatus(), 405) + self.assertEqual(ret.getHeader('content-type'), None) + self.assertEqual(ret.getHeader('cache-control'), 'max-age=0, no-cache, private') + + def testCache_root_anonymous(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/', + ) + self.assertEqual(ret.getStatus(), 401) + self.assertEqual(ret.getHeader('content-type'), None) + self.assertEqual(ret.getHeader('cache-control'), 'max-age=0, no-cache, private') + + def testCache_traverse_authenticatedAndWrongTraverse(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/ERP5Document_getHateoas?mode=traverse&relative_url=unexisting_module&view=view', + user='ERP5TypeTestCase' + ) + self.assertEqual(ret.getStatus(), 404) + self.assertEqual(ret.getHeader('content-type'), None) + self.assertEqual(ret.getHeader('cache-control'), 'private') + + def testCache_traverse_authenticatedAndTraverse(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/ERP5Document_getHateoas?mode=traverse&relative_url=foo_module&view=view', + user='ERP5TypeTestCase' + ) + self.assertEqual(ret.getStatus(), 200) + self.assertEqual(ret.getHeader('content-type'), 'application/hal+json') + self.assertEqual(ret.getHeader('cache-control'), 'private') + + def testCache_traverse_authenticatedAndTraverseWrongView(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/ERP5Document_getHateoas?mode=traverse&relative_url=foo_module&view=foobarview', + user='ERP5TypeTestCase' + ) + self.assertEqual(ret.getStatus(), 404) + self.assertEqual(ret.getHeader('content-type'), None) + self.assertEqual(ret.getHeader('cache-control'), 'private') + + def testCache_traverse_authenticatedAndSearch(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/ERP5Document_getHateoas?mode=search', + user='ERP5TypeTestCase' + ) + self.assertEqual(ret.getStatus(), 200) + self.assertEqual(ret.getHeader('content-type'), 'application/hal+json') + self.assertEqual(ret.getHeader('cache-control'), 'private') + + def testCache_traverse_authenticatedAndDoAction(self): + ret = self.publish( + self.portal.web_site_module.hateoas.getPath() + '/foo_module/Base_callDialogMethod', + user='ERP5TypeTestCase', + request_method='POST', + stdin=io.BytesIO( + 'field_your_select_action=add Foo&' + + 'form_id=FooModule_viewFooList&' + + 'dialog_id=Base_viewNewContentDialog&' + + 'dialog_method=Base_doAction' + ), + env={'CONTENT_TYPE': 'application/x-www-form-urlencoded'} + ) + self.assertEqual(ret.getStatus(), 201) + self.assertEqual(ret.getHeader('content-type'), 'application/json; charset=utf-8') + self.assertEqual(ret.getHeader('cache-control'), 'private') diff --git a/bt5/erp5_web/ToolTemplateItem/caching_policy_manager.xml b/bt5/erp5_web/ToolTemplateItem/caching_policy_manager.xml index 5c4d980c25..b0876be8f1 100644 --- a/bt5/erp5_web/ToolTemplateItem/caching_policy_manager.xml +++ b/bt5/erp5_web/ToolTemplateItem/caching_policy_manager.xml @@ -1506,7 +1506,7 @@ <dictionary> <item> <key> <string>text</string> </key> - <value> <string>python: member is not None and (lambda x: x is not None and x.getCachingPolicy() =="hateoas")(object.getWebSectionValue()) and request.get("X-HATEOAS-CACHE")</string> </value> + <value> <string>python: (member is not None) and (request.other[\'method\'] == "GET") and (request.environ[\'QUERY_STRING\'] == "") and (lambda x: (x is not None) and (request.other[\'PUBLISHED\'] == x) and (x.getCachingPolicy() == "hateoas"))(object.getWebSectionValue())</string> </value> </item> </dictionary> </pickle> -- 2.30.9