Commit 881b4f4f authored by Romain Courteaud's avatar Romain Courteaud

[erp5_hal_json_style] Add the group_by argument

Allow to query the catalog with the group_by parameter.
parent 88ad795c
...@@ -22,6 +22,7 @@ return context.ERP5Document_getHateoas( ...@@ -22,6 +22,7 @@ return context.ERP5Document_getHateoas(
default_param_json=default_param_json, default_param_json=default_param_json,
form_relative_url=form_relative_url, form_relative_url=form_relative_url,
bulk_list=bulk_list, bulk_list=bulk_list,
group_by=group_by,
sort_on=sort_on, sort_on=sort_on,
local_roles=local_roles, local_roles=local_roles,
selection_domain=selection_domain, selection_domain=selection_domain,
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>REQUEST=None, response=None, view=None, mode=\'root\', query=None, select_list=None, limit=10, local_roles=None, form=None, form_data=None, relative_url=None, list_method=None, default_param_json=None, form_relative_url=None, bulk_list="[]", sort_on=None, selection_domain=None, extra_param_json=None, portal_status_message=\'\', portal_status_level=None</string> </value> <value> <string>REQUEST=None, response=None, view=None, mode=\'root\', query=None, select_list=None, limit=10, local_roles=None, form=None, form_data=None, relative_url=None, list_method=None, default_param_json=None, form_relative_url=None, bulk_list="[]", group_by=None, sort_on=None, selection_domain=None, extra_param_json=None, portal_status_message=\'\', portal_status_level=None</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -293,7 +293,7 @@ url_template_dict = { ...@@ -293,7 +293,7 @@ url_template_dict = {
# Search template will call standard "searchValues" on a document described by `root_url` # Search template will call standard "searchValues" on a document described by `root_url`
"search_template": "%(root_url)s/%(script_id)s?mode=search" + \ "search_template": "%(root_url)s/%(script_id)s?mode=search" + \
"{&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}", "{&query,select_list*,limit*,group_by*,sort_on*,local_roles*,selection_domain*}",
"worklist_template": "%(root_url)s/%(script_id)s?mode=worklist", "worklist_template": "%(root_url)s/%(script_id)s?mode=worklist",
# Custom search comes with Listboxes where "list_method" is specified. We pass even listbox's # Custom search comes with Listboxes where "list_method" is specified. We pass even listbox's
# own URL so the search can resolve template fields for proper rendering/formatting/editability # own URL so the search can resolve template fields for proper rendering/formatting/editability
...@@ -305,7 +305,7 @@ url_template_dict = { ...@@ -305,7 +305,7 @@ url_template_dict = {
"&list_method=%(list_method)s" \ "&list_method=%(list_method)s" \
"&extra_param_json=%(extra_param_json)s" \ "&extra_param_json=%(extra_param_json)s" \
"&default_param_json=%(default_param_json)s" \ "&default_param_json=%(default_param_json)s" \
"{&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}", "{&query,select_list*,limit*,group_by*,sort_on*,local_roles*,selection_domain*}",
# Non-editable searches suppose the search results will be rendered as-is and no template # Non-editable searches suppose the search results will be rendered as-is and no template
# fields will get involved. Unfortunately, fields need to be resolved because of formatting # fields will get involved. Unfortunately, fields need to be resolved because of formatting
# all the time so we abandoned this no_editable version # all the time so we abandoned this no_editable version
...@@ -313,7 +313,7 @@ url_template_dict = { ...@@ -313,7 +313,7 @@ url_template_dict = {
"&relative_url=%(relative_url)s" \ "&relative_url=%(relative_url)s" \
"&list_method=%(list_method)s" \ "&list_method=%(list_method)s" \
"&default_param_json=%(default_param_json)s" \ "&default_param_json=%(default_param_json)s" \
"{&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}", "{&query,select_list*,limit*,group_by*,sort_on*,local_roles*,selection_domain*}",
"new_content_action": "%(root_url)s/%(script_id)s?mode=newContent", "new_content_action": "%(root_url)s/%(script_id)s?mode=newContent",
"bulk_action": "%(root_url)s/%(script_id)s?mode=bulk", "bulk_action": "%(root_url)s/%(script_id)s?mode=bulk",
# XXX View is set by default to empty # XXX View is set by default to empty
...@@ -1591,6 +1591,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, ...@@ -1591,6 +1591,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
catalog_kw = { catalog_kw = {
"local_roles": local_roles, "local_roles": local_roles,
"group_by_list": None,
"sort_on": () # default is an empty tuple "sort_on": () # default is an empty tuple
} }
if default_param_json is not None: if default_param_json is not None:
...@@ -1636,6 +1637,14 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, ...@@ -1636,6 +1637,14 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
else: else:
catalog_kw['sort_on'] = [parseSortOn(sort_on), ] catalog_kw['sort_on'] = [parseSortOn(sort_on), ]
if group_by is not None:
if isinstance(group_by, list):
catalog_kw['group_by_list'] = group_by
else:
catalog_kw['group_by_list'] = [str(group_by)]
# Include select, as user may want to count
catalog_kw["select_list"] = select_list
if limit: if limit:
catalog_kw["limit"] = limit catalog_kw["limit"] = limit
if is_rendering_listbox and not has_listbox_a_count_method: if is_rendering_listbox and not has_listbox_a_count_method:
...@@ -1724,6 +1733,8 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None, ...@@ -1724,6 +1733,8 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
'_selection_domain': selection_domain, '_selection_domain': selection_domain,
'_limit': limit, '_limit': limit,
'_select_list': select_list, '_select_list': select_list,
'_group_by': group_by,
'_sort_on': sort_on,
'_embedded': {} '_embedded': {}
}) })
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>REQUEST=None, response=None, view=None, mode=\'root\', query=None, select_list=None, limit=10, local_roles=None, form=None, form_data=None, relative_url=None, restricted=0, list_method=None, default_param_json=None, form_relative_url=None, bulk_list="[]", sort_on=None, selection_domain=None, extra_param_json=None, portal_status_message=\'\', portal_status_level=None</string> </value> <value> <string>REQUEST=None, response=None, view=None, mode=\'root\', query=None, select_list=None, limit=10, local_roles=None, form=None, form_data=None, relative_url=None, restricted=0, list_method=None, default_param_json=None, form_relative_url=None, bulk_list="[]", group_by=None, sort_on=None, selection_domain=None, extra_param_json=None, portal_status_message=\'\', portal_status_level=None</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -417,7 +417,7 @@ class TestERP5Document_getHateoas_mode_root(ERP5HALJSONStyleSkinsMixin): ...@@ -417,7 +417,7 @@ class TestERP5Document_getHateoas_mode_root(ERP5HALJSONStyleSkinsMixin):
self.assertEqual(result_dict['_links']['type']['name'], document.getPortalType()) self.assertEqual(result_dict['_links']['type']['name'], document.getPortalType())
self.assertEqual(result_dict['_links']['raw_search']['href'], self.assertEqual(result_dict['_links']['raw_search']['href'],
"%s/web_site_module/hateoas/ERP5Document_getHateoas?mode=search{&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}" % self.portal.absolute_url()) "%s/web_site_module/hateoas/ERP5Document_getHateoas?mode=search{&query,select_list*,limit*,group_by*,sort_on*,local_roles*,selection_domain*}" % self.portal.absolute_url())
self.assertEqual(result_dict['_links']['raw_search']['templated'], True) self.assertEqual(result_dict['_links']['raw_search']['templated'], True)
self.assertEqual(result_dict['_links']['raw_search']['name'], "Raw Search") self.assertEqual(result_dict['_links']['raw_search']['name'], "Raw Search")
...@@ -633,7 +633,7 @@ class TestERP5Document_getHateoas_mode_traverse(ERP5HALJSONStyleSkinsMixin): ...@@ -633,7 +633,7 @@ class TestERP5Document_getHateoas_mode_traverse(ERP5HALJSONStyleSkinsMixin):
self.assertEqual(result_dict['_embedded']['_view']['listbox']['editable_column_list'], [['id', 'ID'], ['title', 'Title'], ['quantity', 'quantity'], ['start_date', 'Date']]) self.assertEqual(result_dict['_embedded']['_view']['listbox']['editable_column_list'], [['id', 'ID'], ['title', 'Title'], ['quantity', 'quantity'], ['start_date', 'Date']])
self.assertEqual(result_dict['_embedded']['_view']['listbox']['sort_column_list'], [['id', 'ID'], ['title', 'Title'], ['quantity', 'Quantity'], ['start_date', 'Date']]) self.assertEqual(result_dict['_embedded']['_view']['listbox']['sort_column_list'], [['id', 'ID'], ['title', 'Title'], ['quantity', 'Quantity'], ['start_date', 'Date']])
self.assertEqual(result_dict['_embedded']['_view']['listbox']['list_method_template'], self.assertEqual(result_dict['_embedded']['_view']['listbox']['list_method_template'],
'%s/web_site_module/hateoas/ERP5Document_getHateoas?mode=search&relative_url=foo_module%%2F%s&form_relative_url=portal_skins/erp5_ui_test/Foo_view/listbox&list_method=objectValues&extra_param_json=eyJmb3JtX2lkIjogIkZvb192aWV3In0=&default_param_json=eyJwb3J0YWxfdHlwZSI6IFsiRm9vIExpbmUiXSwgImlnbm9yZV91bmtub3duX2NvbHVtbnMiOiB0cnVlfQ=={&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}' % (self.portal.absolute_url(), document.getId())) '%s/web_site_module/hateoas/ERP5Document_getHateoas?mode=search&relative_url=foo_module%%2F%s&form_relative_url=portal_skins/erp5_ui_test/Foo_view/listbox&list_method=objectValues&extra_param_json=eyJmb3JtX2lkIjogIkZvb192aWV3In0=&default_param_json=eyJwb3J0YWxfdHlwZSI6IFsiRm9vIExpbmUiXSwgImlnbm9yZV91bmtub3duX2NvbHVtbnMiOiB0cnVlfQ=={&query,select_list*,limit*,group_by*,sort_on*,local_roles*,selection_domain*}' % (self.portal.absolute_url(), document.getId()))
self.assertEqual(result_dict['_embedded']['_view']['listbox']['domain_root_list'], [['foo_category', 'FooCat'], ['foo_domain', 'FooDomain'], ['not_existing_domain', 'NotExisting']]) self.assertEqual(result_dict['_embedded']['_view']['listbox']['domain_root_list'], [['foo_category', 'FooCat'], ['foo_domain', 'FooDomain'], ['not_existing_domain', 'NotExisting']])
NBSP_prefix = u'\xA0' * 4 NBSP_prefix = u'\xA0' * 4
self.assertEqual(result_dict['_embedded']['_view']['listbox']['domain_dict'], {'foo_domain': [['a', 'a'], ['%sa1' % NBSP_prefix, 'a/a1'], ['%sa2' % NBSP_prefix, 'a/a2'], ['b', 'b']], 'foo_category': [['a', 'a'], ['a/a1', 'a/a1'], ['a/a2', 'a/a2'], ['b', 'b']]}) self.assertEqual(result_dict['_embedded']['_view']['listbox']['domain_dict'], {'foo_domain': [['a', 'a'], ['%sa1' % NBSP_prefix, 'a/a1'], ['%sa2' % NBSP_prefix, 'a/a2'], ['b', 'b']], 'foo_category': [['a', 'a'], ['a/a1', 'a/a1'], ['a/a2', 'a/a2'], ['b', 'b']]})
...@@ -1059,7 +1059,7 @@ class TestERP5Document_getHateoas_mode_traverse(ERP5HALJSONStyleSkinsMixin): ...@@ -1059,7 +1059,7 @@ class TestERP5Document_getHateoas_mode_traverse(ERP5HALJSONStyleSkinsMixin):
self.assertEqual(result_dict['_embedded']['_view']['report_section_list'][1]['listbox']['editable_column_list'], [['time', 'Time'], ['comment', 'Comment'], ['error_message', 'Error Message']]) self.assertEqual(result_dict['_embedded']['_view']['report_section_list'][1]['listbox']['editable_column_list'], [['time', 'Time'], ['comment', 'Comment'], ['error_message', 'Error Message']])
self.assertEqual(result_dict['_embedded']['_view']['report_section_list'][1]['listbox']['sort_column_list'], []) self.assertEqual(result_dict['_embedded']['_view']['report_section_list'][1]['listbox']['sort_column_list'], [])
self.assertEqual(result_dict['_embedded']['_view']['report_section_list'][1]['listbox']['list_method_template'], self.assertEqual(result_dict['_embedded']['_view']['report_section_list'][1]['listbox']['list_method_template'],
'%s/web_site_module/hateoas/ERP5Document_getHateoas?mode=search&relative_url=foo_module%%2F%s&form_relative_url=portal_skins/erp5_core/Base_viewWorkflowHistory/listbox&list_method=Base_getWorkflowHistoryItemList&extra_param_json=eyJmb3JtX2lkIjogIkJhc2Vfdmlld1dvcmtmbG93SGlzdG9yeSJ9&default_param_json=eyJ3b3JrZmxvd19pZCI6ICJmb29fd29ya2Zsb3ciLCAiY2hlY2tlZF9wZXJtaXNzaW9uIjogIlZpZXciLCAid29ya2Zsb3dfdGl0bGUiOiAiRm9vIFdvcmtmbG93IiwgImlnbm9yZV91bmtub3duX2NvbHVtbnMiOiB0cnVlfQ=={&query,select_list*,limit*,sort_on*,local_roles*,selection_domain*}' % (self.portal.absolute_url(), document.getId())) '%s/web_site_module/hateoas/ERP5Document_getHateoas?mode=search&relative_url=foo_module%%2F%s&form_relative_url=portal_skins/erp5_core/Base_viewWorkflowHistory/listbox&list_method=Base_getWorkflowHistoryItemList&extra_param_json=eyJmb3JtX2lkIjogIkJhc2Vfdmlld1dvcmtmbG93SGlzdG9yeSJ9&default_param_json=eyJ3b3JrZmxvd19pZCI6ICJmb29fd29ya2Zsb3ciLCAiY2hlY2tlZF9wZXJtaXNzaW9uIjogIlZpZXciLCAid29ya2Zsb3dfdGl0bGUiOiAiRm9vIFdvcmtmbG93IiwgImlnbm9yZV91bmtub3duX2NvbHVtbnMiOiB0cnVlfQ=={&query,select_list*,limit*,group_by*,sort_on*,local_roles*,selection_domain*}' % (self.portal.absolute_url(), document.getId()))
@simulate('Base_getRequestUrl', '*args, **kwargs', @simulate('Base_getRequestUrl', '*args, **kwargs',
...@@ -1148,6 +1148,8 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin): ...@@ -1148,6 +1148,8 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
self.assertEqual(result_dict['_query'], None) self.assertEqual(result_dict['_query'], None)
self.assertEqual(result_dict['_local_roles'], None) self.assertEqual(result_dict['_local_roles'], None)
self.assertEqual(result_dict['_select_list'], []) self.assertEqual(result_dict['_select_list'], [])
self.assertEqual(result_dict['_group_by'], None)
self.assertEqual(result_dict['_sort_on'], None)
self.assertEqual(len(result_dict['_embedded']['contents']), 10) self.assertEqual(len(result_dict['_embedded']['contents']), 10)
self.assertEqual(result_dict['_embedded']['contents'][0]["_links"]["self"]["href"][:12], "urn:jio:get:") self.assertEqual(result_dict['_embedded']['contents'][0]["_links"]["self"]["href"][:12], "urn:jio:get:")
...@@ -1174,6 +1176,8 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin): ...@@ -1174,6 +1176,8 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
self.assertEqual(result_dict['_query'], None) self.assertEqual(result_dict['_query'], None)
self.assertEqual(result_dict['_local_roles'], None) self.assertEqual(result_dict['_local_roles'], None)
self.assertEqual(result_dict['_select_list'], []) self.assertEqual(result_dict['_select_list'], [])
self.assertEqual(result_dict['_group_by'], None)
self.assertEqual(result_dict['_sort_on'], None)
self.assertEqual(len(result_dict['_embedded']['contents']), 1) self.assertEqual(len(result_dict['_embedded']['contents']), 1)
self.assertEqual(result_dict['_embedded']['contents'][0]["_links"]["self"]["href"][:12], "urn:jio:get:") self.assertEqual(result_dict['_embedded']['contents'][0]["_links"]["self"]["href"][:12], "urn:jio:get:")
...@@ -1256,11 +1260,44 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin): ...@@ -1256,11 +1260,44 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
self.assertEqual(result_dict['_query'], None) self.assertEqual(result_dict['_query'], None)
self.assertEqual(result_dict['_local_roles'], ["Assignor", "Assignee"]) self.assertEqual(result_dict['_local_roles'], ["Assignor", "Assignee"])
self.assertEqual(result_dict['_select_list'], []) self.assertEqual(result_dict['_select_list'], [])
self.assertEqual(result_dict['_group_by'], None)
self.assertEqual(result_dict['_sort_on'], None)
self.assertEqual(len(result_dict['_embedded']['contents']), 0) self.assertEqual(len(result_dict['_embedded']['contents']), 0)
# No count if not in the listbox context currently # No count if not in the listbox context currently
self.assertEqual(result_dict['_embedded'].get('count', None), None) self.assertEqual(result_dict['_embedded'].get('count', None), None)
@simulate('Base_getRequestUrl', '*args, **kwargs',
'return "http://example.org/bar"')
@simulate('Base_getRequestHeader', '*args, **kwargs',
'return "application/hal+json"')
@changeSkin('Hal')
def test_getHateoas_group_by_param(self):
fake_request = do_fake_request("GET")
result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(REQUEST=fake_request, mode="search",
select_list=['count(*)'],
query='portal_type:"Base Category"',
group_by=["portal_type"])
self.assertEquals(fake_request.RESPONSE.status, 200)
self.assertEquals(fake_request.RESPONSE.getHeader('Content-Type'),
"application/hal+json"
)
result_dict = json.loads(result)
self.assertEqual(result_dict['_links']['self'], {"href": "http://example.org/bar"})
self.assertEqual(result_dict['_debug'], "search")
self.assertEqual(result_dict['_limit'], 10)
self.assertEqual(result_dict['_query'], 'portal_type:"Base Category"')
self.assertEqual(result_dict['_local_roles'], None)
self.assertEqual(result_dict['_select_list'], ['count(*)'])
self.assertEqual(result_dict['_group_by'], ["portal_type"])
self.assertEqual(result_dict['_sort_on'], None)
self.assertEqual(len(result_dict['_embedded']['contents']), 1)
self.assertTrue(10 < result_dict['_embedded']['contents'][0]['count(*)'] < 1000)
# No count if not in the listbox context currently
self.assertEqual(result_dict['_embedded'].get('count', None), None)
@simulate('Base_getRequestUrl', '*args, **kwargs', @simulate('Base_getRequestUrl', '*args, **kwargs',
'return "http://example.org/bar"') 'return "http://example.org/bar"')
@simulate('Base_getRequestHeader', '*args, **kwargs', @simulate('Base_getRequestHeader', '*args, **kwargs',
...@@ -1282,6 +1319,8 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin): ...@@ -1282,6 +1319,8 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
self.assertEqual(result_dict['_local_roles'], None) self.assertEqual(result_dict['_local_roles'], None)
self.assertEqual(result_dict['_selection_domain'], '{"foo_category": "a/a2"}') self.assertEqual(result_dict['_selection_domain'], '{"foo_category": "a/a2"}')
self.assertEqual(result_dict['_select_list'], []) self.assertEqual(result_dict['_select_list'], [])
self.assertEqual(result_dict['_group_by'], None)
self.assertEqual(result_dict['_sort_on'], None)
self.assertEqual(len(result_dict['_embedded']['contents']), 1) self.assertEqual(len(result_dict['_embedded']['contents']), 1)
self.assertEqual(result_dict['_embedded']['contents'][0]["_links"]["self"]["href"], "urn:jio:get:portal_categories/foo_category/a/a2") self.assertEqual(result_dict['_embedded']['contents'][0]["_links"]["self"]["href"], "urn:jio:get:portal_categories/foo_category/a/a2")
...@@ -1310,6 +1349,8 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin): ...@@ -1310,6 +1349,8 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
self.assertEqual(result_dict['_local_roles'], None) self.assertEqual(result_dict['_local_roles'], None)
self.assertEqual(result_dict['_selection_domain'], '{"foo_domain": "a/a1"}') self.assertEqual(result_dict['_selection_domain'], '{"foo_domain": "a/a1"}')
self.assertEqual(result_dict['_select_list'], []) self.assertEqual(result_dict['_select_list'], [])
self.assertEqual(result_dict['_group_by'], None)
self.assertEqual(result_dict['_sort_on'], None)
self.assertEqual(len(result_dict['_embedded']['contents']), 1) self.assertEqual(len(result_dict['_embedded']['contents']), 1)
self.assertEqual(result_dict['_embedded']['contents'][0]["_links"]["self"]["href"], "urn:jio:get:portal_categories/foo_category/a/a1") self.assertEqual(result_dict['_embedded']['contents'][0]["_links"]["self"]["href"], "urn:jio:get:portal_categories/foo_category/a/a1")
......
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