Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Tomáš Peterka
erp5
Commits
6f2270f9
Commit
6f2270f9
authored
Nov 09, 2017
by
Tomáš Peterka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[hal_json_style] Unittest exotic results of search method
parent
6cf2326c
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
116 additions
and
27 deletions
+116
-27
bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
...rtal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
+24
-23
bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
...plateItem/portal_components/test.erp5.testHalJsonStyle.py
+92
-4
No files found.
bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
View file @
6f2270f9
...
@@ -12,10 +12,13 @@ Only in mode == 'search'
...
@@ -12,10 +12,13 @@ Only in mode == 'search'
:param query:
:param query:
:param select_list:
:param select_list:
:param limit:
:param limit:
:param form_relative_url: {str} relative URL of a form FIELD issuing the search (listbox/relation field...)
Only in mode == 'form'
Only in mode == 'form'
:param form:
:param form:
Only in mode == 'traverse'
TBD.
TBD.
"""
"""
from
ZTUtils
import
make_query
from
ZTUtils
import
make_query
...
@@ -143,11 +146,7 @@ def getRealRelativeUrl(document):
...
@@ -143,11 +146,7 @@ def getRealRelativeUrl(document):
def
getFormRelativeUrl
(
form
):
def
getFormRelativeUrl
(
form
):
return
portal
.
portal_catalog
(
return
portal
.
portal_catalog
(
portal_type
=
ComplexQuery
(
portal_type
=
(
"ERP5 Form"
,
"ERP5 Report"
),
Query
(
portal_type
=
"ERP5 Form"
),
Query
(
portal_type
=
"ERP5 Report"
),
logical_operator
=
'or'
),
uid
=
form
.
getUid
(),
uid
=
form
.
getUid
(),
id
=
form
.
getId
(),
id
=
form
.
getId
(),
limit
=
1
,
limit
=
1
,
...
@@ -416,7 +415,7 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
...
@@ -416,7 +415,7 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
# we abandoned Selections in RJS thus we mix selection query parameters into
# we abandoned Selections in RJS thus we mix selection query parameters into
# listbox's default parameters
# listbox's default parameters
default_params
.
update
(
selection_params
)
default_params
.
update
(
selection_params
)
lines
=
field
.
get_value
(
'lines'
)
lines
=
field
.
get_value
(
'lines'
)
list_method_name
=
traversed_document
.
Listbox_getListMethodName
(
field
)
list_method_name
=
traversed_document
.
Listbox_getListMethodName
(
field
)
...
@@ -449,7 +448,7 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
...
@@ -449,7 +448,7 @@ def renderField(traversed_document, field, form, value=None, meta_type=None, key
if
list_method_param
in
REQUEST
and
list_method_param
not
in
list_method_query_dict
:
if
list_method_param
in
REQUEST
and
list_method_param
not
in
list_method_query_dict
:
list_method_query_dict
[
list_method_param
]
=
REQUEST
.
get
(
list_method_param
)
list_method_query_dict
[
list_method_param
]
=
REQUEST
.
get
(
list_method_param
)
# MIDDLE-DANGEROUS!
# MIDDLE-DANGEROUS!
# In case of reports (later even exports) substitute None for unknown
# In case of reports (later even exports) substitute None for unknown
# parameters. We suppose Python syntax for parameters!
# parameters. We suppose Python syntax for parameters!
# What we do here is literally putting every form field from REQUEST
# What we do here is literally putting every form field from REQUEST
# into search method parameters - this is later put back into REQUEST
# into search method parameters - this is later put back into REQUEST
...
@@ -1169,16 +1168,14 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1169,16 +1168,14 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
"template"
:
True
"template"
:
True
}
}
}
}
# Define document action
# Define document action
if
action_dict
:
if
action_dict
:
result_dict
[
'_actions'
]
=
action_dict
result_dict
[
'_actions'
]
=
action_dict
elif
mode
==
'search'
:
elif
mode
==
'search'
:
#################################################
#################################################
# Portal catalog search
# Portal catalog search
#
#
# Possible call arguments example:
# Possible call arguments example:
# form_relative_url: portal_skins/erp5_web/WebSite_view/listbox
# form_relative_url: portal_skins/erp5_web/WebSite_view/listbox
...
@@ -1321,7 +1318,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1321,7 +1318,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
continue
continue
if
result_index
>=
start
+
num_items
:
if
result_index
>=
start
+
num_items
:
break
break
contents_item
=
{}
contents_item
=
{}
contents_list
.
append
(
contents_item
)
contents_list
.
append
(
contents_item
)
...
@@ -1357,14 +1354,14 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1357,14 +1354,14 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
search_property_getter
=
getProtectedProperty
search_property_getter
=
getProtectedProperty
search_property_hasser
=
lambda
doc
,
attr
:
doc
.
hasProperty
(
attr
)
search_property_hasser
=
lambda
doc
,
attr
:
doc
.
hasProperty
(
attr
)
else
:
else
:
# In case of reports the `search_result` can be list of
# In case of reports the `search_result` can be list of
# PythonScripts.standard._Object - a reimplementation of plain dictionary
# PythonScripts.standard._Object - a reimplementation of plain dictionary
# means we are iterating over plain objects
# means we are iterating over plain objects
# list_method must be defined because POPOs can return only that
# list_method must be defined because POPOs can return only that
contents_uid
=
"{}#{:d}"
.
format
(
list_method
,
result_index
)
contents_uid
=
"{}#{:d}"
.
format
(
list_method
,
result_index
)
# JIO requires every item to have _links.self.href so it can construct
# JIO requires every item to have _links.self.href so it can construct
# links to the document. Here we have a object in RAM (which should
# links to the document. Here we have a object in RAM (which should
# never happen!) thus we provide temporary UID
# never happen!) thus we provide temporary UID
contents_relative_url
=
"{}/{}"
.
format
(
traversed_document
.
getRelativeUrl
(),
contents_uid
)
contents_relative_url
=
"{}/{}"
.
format
(
traversed_document
.
getRelativeUrl
(),
contents_uid
)
# property getter must be simple __getattr__ implementation
# property getter must be simple __getattr__ implementation
...
@@ -1377,7 +1374,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1377,7 +1374,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
'self'
:
{
'self'
:
{
"href"
:
default_document_uri_template
%
{
"href"
:
default_document_uri_template
%
{
"root_url"
:
site_root
.
absolute_url
(),
"root_url"
:
site_root
.
absolute_url
(),
"relative_url"
:
contents_relative_url
,
"relative_url"
:
contents_relative_url
,
"script_id"
:
script
.
id
"script_id"
:
script
.
id
},
},
},
},
...
@@ -1389,7 +1386,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1389,7 +1386,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
'key'
:
"%s_uid:list"
%
listbox_field_id
,
'key'
:
"%s_uid:list"
%
listbox_field_id
,
'value'
:
contents_uid
'value'
:
contents_uid
}
}
# render whole field in contents_item or at least search result value
# render whole field in contents_item or at least search result value
for
select
in
select_list
:
for
select
in
select_list
:
if
editable_field_dict
.
has_key
(
select
):
if
editable_field_dict
.
has_key
(
select
):
# cell has a Form Field template thus render it using the field
# cell has a Form Field template thus render it using the field
...
@@ -1404,9 +1401,10 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1404,9 +1401,10 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
contents_item
[
select
]
=
renderField
(
contents_item
[
select
]
=
renderField
(
traversed_document
,
traversed_document
,
editable_field_dict
[
select
],
editable_field_dict
[
select
],
listbox_form
,
listbox_form
,
value
=
default_field_value
,
value
=
default_field_value
,
key
=
'field_%s_%s'
%
(
editable_field_dict
[
select
].
id
,
contents_uid
))
key
=
'field_%s_%s'
%
(
editable_field_dict
[
select
].
id
,
contents_uid
))
REQUEST
.
other
.
pop
(
'cell'
,
None
)
REQUEST
.
other
.
pop
(
'cell'
,
None
)
else
:
else
:
...
@@ -1414,19 +1412,20 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1414,19 +1412,20 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
# value by resolving value in the correct order. The code is copy&pasted
# value by resolving value in the correct order. The code is copy&pasted
# from ListBoxRendererLine.getValueList because it is universal
# from ListBoxRendererLine.getValueList because it is universal
contents_value
=
None
contents_value
=
None
if
"."
in
select
:
select
=
select
[
select
.
rindex
(
'.'
)
+
1
:]
if
len
(
select
)
==
0
:
if
not
isinstance
(
select
,
(
str
,
unicode
))
or
len
(
select
)
==
0
:
context
.
log
(
'There is an
empty
column name in {!s}!'
.
format
(
select_list
),
level
=
200
)
context
.
log
(
'There is an
invalid
column name in {!s}!'
.
format
(
select_list
),
level
=
200
)
continue
continue
if
"."
in
select
:
select
=
select
[
select
.
rindex
(
'.'
)
+
1
:]
# 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
if
search_property_hasser
(
raw_search_result
,
select
):
if
search_property_hasser
(
raw_search_result
,
select
):
contents_value
=
search_property_getter
(
raw_search_result
,
select
)
contents_value
=
search_property_getter
(
raw_search_result
,
select
)
...
@@ -1470,7 +1469,9 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
...
@@ -1470,7 +1469,9 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
has_brain_param
=
False
has_brain_param
=
False
if
hasattr
(
contents_value
,
"params"
):
if
hasattr
(
contents_value
,
"params"
):
has_mandatory_param
=
any
(
map
(
lambda
param
:
'='
not
in
param
and
'*'
not
in
param
,
has_mandatory_param
=
any
(
map
(
lambda
param
:
'='
not
in
param
and
'*'
not
in
param
,
contents_value
.
params
().
split
(
","
)))
contents_value
.
params
().
split
(
","
)))
\
if
contents_value
.
params
()
\
else
False
# because any([]) == True
has_brain_param
=
"brain"
in
contents_value
.
params
()
has_brain_param
=
"brain"
in
contents_value
.
params
()
try
:
try
:
if
has_mandatory_param
:
if
has_mandatory_param
:
...
...
bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
View file @
6f2270f9
...
@@ -66,14 +66,26 @@ def simulate(script_id, params_string, code_string):
...
@@ -66,14 +66,26 @@ def simulate(script_id, params_string, code_string):
return
decorated
return
decorated
return
upperWrap
return
upperWrap
def
createIndexedDocument
():
def
wipeFolder
(
folder
):
"""Create a Foo document inside Foo module and pass it as "document" argument into wrapped function."""
folder
.
deleteContent
(
list
(
folder
.
objectIds
()))
transaction
.
commit
()
def
createIndexedDocument
(
quantity
=
1
):
"""Create `quantity` Foo document(s) in Foo module and pass it as `document(_list)` argument into the wrapped function."""
def
decorator
(
func
):
def
decorator
(
func
):
def
wrapped
(
self
,
*
args
,
**
kwargs
):
def
wrapped
(
self
,
*
args
,
**
kwargs
):
kwargs
.
update
(
document
=
self
.
_makeDocument
())
wipeFolder
(
self
.
portal
.
foo_module
)
if
quantity
<=
1
:
kwargs
.
update
(
document
=
self
.
_makeDocument
())
else
:
kwargs
.
update
(
document_list
=
[
self
.
_makeDocument
()
for
_
in
range
(
quantity
)])
self
.
portal
.
portal_caches
.
clearAllCache
()
self
.
portal
.
portal_caches
.
clearAllCache
()
self
.
tic
()
self
.
tic
()
return
func
(
self
,
*
args
,
**
kwargs
)
try
:
return
func
(
self
,
*
args
,
**
kwargs
)
finally
:
wipeFolder
(
self
.
portal
.
foo_module
)
self
.
tic
()
# unindex
return
wrapped
return
wrapped
return
decorator
return
decorator
...
@@ -1074,6 +1086,82 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
...
@@ -1074,6 +1086,82 @@ class TestERP5Document_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
mode="search",
mode="search",
default_param_json='
eyJcdTAwZWEiOiAiXHUwMGU4In0
=
')
default_param_json='
eyJcdTAwZWEiOiAiXHUwMGU4In0
=
')
@simulate('
Base_getRequestUrl
', '
*
args
,
**
kwargs
', '
return
"http://example.org/bar"')
@simulate('
Base_getRequestHeader
', '
*
args
,
**
kwargs
', '
return
"application/hal+json"')
@simulate('
Test_listObjects
', '
*
args
,
**
kwargs
', """
from Products.PythonScripts.standard import Object
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()
""")
@simulate('
Test_listCatalog
', '
*
args
,
**
kwargs
', """
return context.getPortalObject().portal_catalog(portal_type='
Foo
', sort_on=[('
id
', '
ASC
')])
""")
@createIndexedDocument(quantity=2)
@changeSkin('
Hal
')
def test_getHateoas_exotic_search_results(self, document_list):
"""Test that ingestion of `list_method` result does not fail.
The only limit for the result of `list_method` is that it should be an iterable.
Practically, because we code in python, it can be any object.
"""
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_listObjects
',
select_list=['
credit_price
', '
debit_price
']
)
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(len(result_dict['
_embedded
']['
contents
']), 2)
self.assertEqual(result_dict['
_embedded
']['
contents
'][0]['
debit_price
'], 1000.0)
self.assertEqual(result_dict['
_embedded
']['
contents
'][0]['
credit_price
'], 100.0)
self.assertEqual(result_dict['
_embedded
']['
contents
'][1]['
debit_price
'], 10.0)
self.assertEqual(result_dict['
_embedded
']['
contents
'][1]['
credit_price
'], 0.0)
# Render a Document using Form Field template (only for field '
id
')
result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(
REQUEST=fake_request,
mode="search",
local_roles=["Assignor", "Assignee"],
list_method='
Test_listProducts
',
select_list=['
id
'],
form_relative_url='
portal_skins
/
erp5_ui_test
/
FooModule_viewFooList
/
listbox
'
)
result_dict = json.loads(result)
self.assertEqual(2, len(result_dict['
_embedded
']['
contents
']))
self.assertIn("field_listbox", result_dict['
_embedded
']['
contents
'][0]['
id
']['
key
'])
self.assertEqual("StringField", result_dict['
_embedded
']['
contents
'][0]['
id
']['
type
'])
self.assertEqual(document_list[0].getId(), result_dict['
_embedded
']['
contents
'][0]['
id
']['
default
'])
self.assertIn("field_listbox", result_dict['
_embedded
']['
contents
'][1]['
id
']['
key
'])
self.assertEqual("StringField", result_dict['
_embedded
']['
contents
'][1]['
id
']['
type
'])
self.assertEqual(document_list[1].getId(), result_dict['
_embedded
']['
contents
'][1]['
id
']['
default
'])
# Test rendering without form template of attribute, getterm and a script
result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(
REQUEST=fake_request,
mode="search",
local_roles=["Assignor", "Assignee"],
list_method='
Test_listCatalog
',
select_list=['
title
', '
Foo_getLocalTitle
', '
getTotalQuantity
'] # property, Script, method
)
result_dict = json.loads(result)
self.assertEqual(len(result_dict['
_embedded
']['
contents
']), 2)
self.assertEqual(result_dict['
_embedded
']['
contents
'][0]['
title
'].encode('
utf
-
8
'), document_list[0].getTitle())
self.assertEqual(result_dict['
_embedded
']['
contents
'][0]['
Foo_getLocalTitle
'], None)
self.assertEqual(result_dict['
_embedded
']['
contents
'][0]['
getTotalQuantity
'], 0)
self.assertEqual(result_dict['
_embedded
']['
contents
'][1]['
title
'].encode('
utf
-
8
'), document_list[1].getTitle())
self.assertEqual(result_dict['
_embedded
']['
contents
'][1]['
Foo_getLocalTitle
'], None)
self.assertEqual(result_dict['
_embedded
']['
contents
'][1]['
getTotalQuantity
'], 0)
class TestERP5Document_getHateoas_mode_bulk(ERP5HALJSONStyleSkinsMixin):
class TestERP5Document_getHateoas_mode_bulk(ERP5HALJSONStyleSkinsMixin):
@simulate('
Base_getRequestHeader
', '
*
args
,
**
kwargs
',
@simulate('
Base_getRequestHeader
', '
*
args
,
**
kwargs
',
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment