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 ebdcba08aeada56a34557f9f575329e539961f1c..777597b1e0dc6aa07d61c15b7a447700c09ed6a5 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 @@ -191,13 +191,22 @@ def getAttrFromAnything(search_result, select, search_property_getter, search_pr if "." in select: select = select[select.rindex('.') + 1:] + # prepare accessor/getter name because this must be the first tried possibility + # getter is preferred way how to obtain properties - property itself is the second + if not select.startswith('get') and select[0] not in string.ascii_uppercase: + # maybe a hidden getter (variable accessible by a getter) + accessor_name = 'get' + UpperCase(select) + else: + # or obvious getter (starts with "get" or Capital letter - Script) + accessor_name = select + # 1. resolve attribute on a raw object (all wrappers removed) using # lowest-level secure getattr method given object type raw_search_result = search_result if hasattr(search_result, 'aq_base'): raw_search_result = search_result.aq_base - - if search_property_hasser(raw_search_result, select): + # BUT! only if there is no accessor (because that is the prefered way) + if search_property_hasser(raw_search_result, select) and not hasattr(raw_search_result, accessor_name): contents_value = search_property_getter(raw_search_result, select) # 2. use the fact that wrappers (brain or acquisition wrapper) use @@ -207,12 +216,6 @@ def getAttrFromAnything(search_result, select, search_property_getter, search_pr unwrapped_search_result = search_result.aq_self if contents_value is None: - if not select.startswith('get') and select[0] not in string.ascii_uppercase: - # maybe a hidden getter (variable accessible by a getter) - accessor_name = 'get' + UpperCase(select) - else: - # or obvious getter (starts with "get" or Capital letter - Script) - accessor_name = select # again we check on a unwrapped object to avoid acquisition resolution # which would certainly find something which we don't want try: 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 0d1b3be9a5581828d9c5508ef5054a7a29e2d21e..1488e122771a5d150c23302263cd86a596822463 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 @@ -990,7 +990,7 @@ return [Object(debit_price=1000.00, credit_price=100.00), Object(debit_price=10.00, credit_price=0.00)] """) @simulate('Test_listProducts', '*args, **kwargs', """ -return context.getPortalObject().foo_module.values() +return context.getPortalObject().foo_module.contentValues() """) @simulate('Test_listCatalog', '*args, **kwargs', """ return context.getPortalObject().portal_catalog(portal_type='Foo', sort_on=[('id', 'ASC')]) @@ -1058,6 +1058,59 @@ return context.getPortalObject().portal_catalog(portal_type='Foo', sort_on=[('id self.assertEqual(result_dict['_embedded']['contents'][1]['getTotalQuantity'], 0) +class TestERP5Person_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin): + """Test HAL_JSON operations on cataloged Persons and other allowed content types of Person Module.""" + + def afterSetUp(self): + self.person = self.portal.person_module.newContent( + portal_type='Person', first_name="Benoit", last_name="Mandelbrot") + self.tic() + + def beforeTearDown(self): + self.portal.person_module.deleteContent(self.person.getId()) + + + @simulate('Base_getRequestUrl', '*args, **kwargs', 'return "http://example.org/bar"') + @simulate('Base_getRequestHeader', '*args, **kwargs', 'return "application/hal+json"') + @simulate('Test_listPersons', '*args, **kwargs', """ +return context.getPortalObject().person_module.contentValues(portal_type="Person") +""") + @simulate('Test_listPersonsCatalog', '*args, **kwargs', """ +return context.getPortalObject().portal_catalog.searchResults(portal_type="Person") +""") + @changeSkin('Hal') + def test_getHateoas_person_title_search(self): + """Person has amazing property of having attribute "title" and "getTitle" with different return values. + + Value resolution must prefer getter over raw attribute. + """ + fake_request = do_fake_request("GET") + + result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas( + REQUEST=fake_request, + mode="search", + local_roles=["Assignor", "Assignee"], + list_method='Test_listPersons', + select_list=['title'] # attribute which must be resolved through getter + ) + result_dict = json.loads(result) + titles = [result['title'] for result in result_dict['_embedded']['contents']] + # getTitle() composes title from first_name and last_name while attribute "title" remains empty + self.assertIn("Benoit Mandelbrot", titles) + + result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas( + REQUEST=fake_request, + mode="search", + local_roles=["Assignor", "Assignee"], + list_method='Test_listPersonsCatalog', + select_list=['title'] # attribute which must be resolved through getter + ) + result_dict = json.loads(result) + titles = [result['title'] for result in result_dict['_embedded']['contents']] + # getTitle() composes title from first_name and last_name while attribute "title" remains empty + self.assertIn("Benoit Mandelbrot", titles) + + class TestERP5PDM_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin): """This class allows ticking for Movements to be picked up by activities."""