Commit b737b469 authored by Tomáš Peterka's avatar Tomáš Peterka Committed by Tomáš Peterka

[hal_json_style] Prefer getter over attribute on raw objects

Fixes bug /#/bug_module/20171211-153B610

/reviewed-on nexedi/erp5!526
parent e579ec15
...@@ -191,13 +191,22 @@ def getAttrFromAnything(search_result, select, search_property_getter, search_pr ...@@ -191,13 +191,22 @@ def getAttrFromAnything(search_result, select, search_property_getter, search_pr
if "." in select: if "." in select:
select = select[select.rindex('.') + 1:] 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 # 1. resolve attribute on a raw object (all wrappers removed) using
# lowest-level secure getattr method given object type # lowest-level secure getattr method given object type
raw_search_result = search_result raw_search_result = search_result
if hasattr(search_result, 'aq_base'): if hasattr(search_result, 'aq_base'):
raw_search_result = search_result.aq_base raw_search_result = search_result.aq_base
# BUT! only if there is no accessor (because that is the prefered way)
if search_property_hasser(raw_search_result, select): 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) contents_value = search_property_getter(raw_search_result, select)
# 2. use the fact that wrappers (brain or acquisition wrapper) use # 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 ...@@ -207,12 +216,6 @@ def getAttrFromAnything(search_result, select, search_property_getter, search_pr
unwrapped_search_result = search_result.aq_self unwrapped_search_result = search_result.aq_self
if contents_value is None: 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 # again we check on a unwrapped object to avoid acquisition resolution
# which would certainly find something which we don't want # which would certainly find something which we don't want
try: try:
......
...@@ -990,7 +990,7 @@ return [Object(debit_price=1000.00, credit_price=100.00), ...@@ -990,7 +990,7 @@ return [Object(debit_price=1000.00, credit_price=100.00),
Object(debit_price=10.00, credit_price=0.00)] Object(debit_price=10.00, credit_price=0.00)]
""") """)
@simulate('Test_listProducts', '*args, **kwargs', """ @simulate('Test_listProducts', '*args, **kwargs', """
return context.getPortalObject().foo_module.values() return context.getPortalObject().foo_module.contentValues()
""") """)
@simulate('Test_listCatalog', '*args, **kwargs', """ @simulate('Test_listCatalog', '*args, **kwargs', """
return context.getPortalObject().portal_catalog(portal_type='Foo', sort_on=[('id', 'ASC')]) 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 ...@@ -1058,6 +1058,59 @@ return context.getPortalObject().portal_catalog(portal_type='Foo', sort_on=[('id
self.assertEqual(result_dict['_embedded']['contents'][1]['getTotalQuantity'], 0) 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): class TestERP5PDM_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
"""This class allows ticking for Movements to be picked up by activities.""" """This class allows ticking for Movements to be picked up by activities."""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment