Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.core
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
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Romain Courteaud
slapos.core
Commits
2f2aa9bb
Commit
2f2aa9bb
authored
Dec 12, 2024
by
Romain Courteaud
🐙
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
erp5_json_rpc_api: validate output schema
parent
51ab9745
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
142 additions
and
5 deletions
+142
-5
master/bt5/erp5_json_rpc_api/DocumentTemplateItem/portal_components/document.erp5.JsonRpcAPIService.py
...Item/portal_components/document.erp5.JsonRpcAPIService.py
+19
-3
master/bt5/erp5_json_rpc_api/TestTemplateItem/portal_components/test.erp5.testJsonRpcAPIService.py
...Item/portal_components/test.erp5.testJsonRpcAPIService.py
+123
-2
No files found.
master/bt5/erp5_json_rpc_api/DocumentTemplateItem/portal_components/document.erp5.JsonRpcAPIService.py
View file @
2f2aa9bb
...
@@ -335,9 +335,9 @@ class JsonRpcAPIService(OpenAPIService):
...
@@ -335,9 +335,9 @@ class JsonRpcAPIService(OpenAPIService):
operation
=
self
.
getMatchingOperation
(
request
)
operation
=
self
.
getMatchingOperation
(
request
)
if
operation
is
None
:
if
operation
is
None
:
raise
NotFound
()
raise
NotFound
()
method
=
getattr
(
self
,
operation
)
#self.getMethodForOperation(operation)
json_form
=
getattr
(
self
,
operation
)
#self.getMethodForOperation(operation)
if
method
.
getPortalType
()
!=
'JSON Form'
:
if
json_form
.
getPortalType
()
!=
'JSON Form'
:
raise
ValueError
(
'%s is not a JSON Form'
%
operation
)
raise
ValueError
(
'%s is not a JSON Form'
%
operation
)
# parameters = self.extractParametersFromRequest(operation, request)
# parameters = self.extractParametersFromRequest(operation, request)
try
:
try
:
...
@@ -348,16 +348,32 @@ class JsonRpcAPIService(OpenAPIService):
...
@@ -348,16 +348,32 @@ class JsonRpcAPIService(OpenAPIService):
raise
JsonRpcAPINotJsonDictContent
(
"Did not received a JSON Object"
)
raise
JsonRpcAPINotJsonDictContent
(
"Did not received a JSON Object"
)
try
:
try
:
result
=
method
(
json_data
=
json_data
,
list_error
=
False
)
#**parameters)
result
=
json_form
(
json_data
=
json_data
,
list_error
=
False
)
#**parameters)
except
jsonschema
.
exceptions
.
ValidationError
as
e
:
except
jsonschema
.
exceptions
.
ValidationError
as
e
:
raise
JsonRpcAPIInvalidJsonDictContent
(
str
(
e
))
raise
JsonRpcAPIInvalidJsonDictContent
(
str
(
e
))
response
=
request
.
RESPONSE
response
=
request
.
RESPONSE
output_schema
=
json_form
.
getOutputSchema
()
# XXX Hardcoded JSONForm behaviour
# XXX Hardcoded JSONForm behaviour
if
(
result
==
"Nothing to do"
)
or
(
not
result
):
if
(
result
==
"Nothing to do"
)
or
(
not
result
):
# If there is no output, ensure no output schema is defined
if
output_schema
:
raise
ValueError
(
'%s has an output schema but response is empty'
%
operation
)
result
=
{
result
=
{
'status'
:
200
,
'status'
:
200
,
'type'
:
'success-type'
,
'type'
:
'success-type'
,
'title'
:
'query completed'
'title'
:
'query completed'
}
}
else
:
if
not
output_schema
:
raise
ValueError
(
'%s does not have an output schema but response is not empty'
%
operation
)
# Ensure the response matches the output schema
try
:
jsonschema
.
validate
(
result
,
json
.
loads
(
output_schema
),
format_checker
=
jsonschema
.
FormatChecker
()
)
except
jsonschema
.
exceptions
.
ValidationError
as
e
:
raise
ValueError
(
e
.
message
)
response
.
setHeader
(
"Content-Type"
,
"application/json"
)
response
.
setHeader
(
"Content-Type"
,
"application/json"
)
return
json
.
dumps
(
result
).
encode
()
return
json
.
dumps
(
result
).
encode
()
master/bt5/erp5_json_rpc_api/TestTemplateItem/portal_components/test.erp5.testJsonRpcAPIService.py
View file @
2f2aa9bb
...
@@ -35,12 +35,13 @@ class JsonRpcAPITestCase(ERP5TypeTestCase):
...
@@ -35,12 +35,13 @@ class JsonRpcAPITestCase(ERP5TypeTestCase):
_action_list_text
=
''
_action_list_text
=
''
def
addJSONForm
(
self
,
script_id
,
input_json_schema
=
None
,
def
addJSONForm
(
self
,
script_id
,
input_json_schema
=
None
,
after_method_id
=
None
):
after_method_id
=
None
,
output_json_schema
=
None
):
self
.
portal
.
portal_callables
.
newContent
(
self
.
portal
.
portal_callables
.
newContent
(
portal_type
=
'JSON Form'
,
portal_type
=
'JSON Form'
,
id
=
script_id
,
id
=
script_id
,
text_content
=
input_json_schema
,
text_content
=
input_json_schema
,
after_method_id
=
after_method_id
after_method_id
=
after_method_id
,
output_schema
=
output_json_schema
)
)
self
.
tic
()
self
.
tic
()
self
.
_json_form_id_to_cleanup
.
append
(
script_id
)
self
.
_json_form_id_to_cleanup
.
append
(
script_id
)
...
@@ -410,6 +411,126 @@ error.handling.callable | JsonRpcService_testExample'''
...
@@ -410,6 +411,126 @@ error.handling.callable | JsonRpcService_testExample'''
})
})
self
.
assertNotEqual
(
self
.
portal
.
getTitle
(),
"ooops"
)
self
.
assertNotEqual
(
self
.
portal
.
getTitle
(),
"ooops"
)
def
test_requestErrorHandling_noOutputWithOutputSchema
(
self
):
self
.
addPythonScript
(
'JsonRpcService_returnNothing'
,
'data_dict, json_form'
,
'context.getPortalObject().setTitle("ooops")
\
n
'
)
self
.
addJSONForm
(
'JsonRpcService_testExample'
,
'{}'
,
output_json_schema
=
'''{"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {"foo": {"type": "string"}}}'''
,
after_method_id
=
'JsonRpcService_returnNothing'
,
)
response
=
self
.
publish
(
self
.
connector
.
getPath
()
+
'/error.handling.callable'
,
user
=
'ERP5TypeTestCase'
,
request_method
=
'POST'
,
stdin
=
io
.
BytesIO
(
'{}'
.
encode
()),
env
=
{
'CONTENT_TYPE'
:
'application/json'
})
self
.
assertEqual
(
response
.
getStatus
(),
500
)
self
.
assertEqual
(
response
.
getHeader
(
'content-type'
),
'application/json'
)
self
.
assertEqual
(
json
.
loads
(
response
.
getBody
()),
{
"type"
:
"unknown-error"
,
"title"
:
"ValueError: JsonRpcService_testExample has an output schema but response is empty"
})
self
.
assertNotEqual
(
self
.
portal
.
getTitle
(),
"ooops"
)
def
test_requestErrorHandling_emptyOutputWithOutputSchema
(
self
):
self
.
addPythonScript
(
'JsonRpcService_returnNothing'
,
'data_dict, json_form'
,
'context.getPortalObject().setTitle("ooops")
\
n
'
'return {}'
)
self
.
addJSONForm
(
'JsonRpcService_testExample'
,
'{}'
,
output_json_schema
=
'''{"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {"foo": {"type": "string"}}}'''
,
after_method_id
=
'JsonRpcService_returnNothing'
,
)
response
=
self
.
publish
(
self
.
connector
.
getPath
()
+
'/error.handling.callable'
,
user
=
'ERP5TypeTestCase'
,
request_method
=
'POST'
,
stdin
=
io
.
BytesIO
(
'{}'
.
encode
()),
env
=
{
'CONTENT_TYPE'
:
'application/json'
})
self
.
assertEqual
(
response
.
getStatus
(),
500
)
self
.
assertEqual
(
response
.
getHeader
(
'content-type'
),
'application/json'
)
self
.
assertEqual
(
json
.
loads
(
response
.
getBody
()),
{
"type"
:
"unknown-error"
,
"title"
:
"ValueError: JsonRpcService_testExample has an output schema but response is empty"
})
self
.
assertNotEqual
(
self
.
portal
.
getTitle
(),
"ooops"
)
def
test_requestErrorHandling_invalidOutputWithOutputSchema
(
self
):
self
.
addPythonScript
(
'JsonRpcService_returnNothing'
,
'data_dict, json_form'
,
'context.getPortalObject().setTitle("ooops")
\
n
'
'return {"foo": 2}'
)
self
.
addJSONForm
(
'JsonRpcService_testExample'
,
'{}'
,
output_json_schema
=
'''{"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {"foo": {"type": "string"}}}'''
,
after_method_id
=
'JsonRpcService_returnNothing'
,
)
response
=
self
.
publish
(
self
.
connector
.
getPath
()
+
'/error.handling.callable'
,
user
=
'ERP5TypeTestCase'
,
request_method
=
'POST'
,
stdin
=
io
.
BytesIO
(
'{}'
.
encode
()),
env
=
{
'CONTENT_TYPE'
:
'application/json'
})
self
.
assertEqual
(
response
.
getStatus
(),
500
)
self
.
assertEqual
(
response
.
getHeader
(
'content-type'
),
'application/json'
)
self
.
assertEqual
(
json
.
loads
(
response
.
getBody
()),
{
"type"
:
"unknown-error"
,
"title"
:
"ValueError: 2 is not of type u'string'"
})
self
.
assertNotEqual
(
self
.
portal
.
getTitle
(),
"ooops"
)
def
test_requestErrorHandling_outputWithoutOutputSchema
(
self
):
self
.
addPythonScript
(
'JsonRpcService_returnNothing'
,
'data_dict, json_form'
,
'context.getPortalObject().setTitle("ooops")
\
n
'
'return {"foo": 2}'
)
self
.
addJSONForm
(
'JsonRpcService_testExample'
,
'{}'
,
after_method_id
=
'JsonRpcService_returnNothing'
,
)
response
=
self
.
publish
(
self
.
connector
.
getPath
()
+
'/error.handling.callable'
,
user
=
'ERP5TypeTestCase'
,
request_method
=
'POST'
,
stdin
=
io
.
BytesIO
(
'{}'
.
encode
()),
env
=
{
'CONTENT_TYPE'
:
'application/json'
})
self
.
assertEqual
(
response
.
getStatus
(),
500
)
self
.
assertEqual
(
response
.
getHeader
(
'content-type'
),
'application/json'
)
self
.
assertEqual
(
json
.
loads
(
response
.
getBody
()),
{
"type"
:
"unknown-error"
,
"title"
:
"ValueError: JsonRpcService_testExample does not have an output schema but response is not empty"
})
self
.
assertNotEqual
(
self
.
portal
.
getTitle
(),
"ooops"
)
class
TestJsonRpcAPICustomErrorHandling
(
JsonRpcAPITestCase
):
class
TestJsonRpcAPICustomErrorHandling
(
JsonRpcAPITestCase
):
_action_list_text
=
'''error.handling.missing.callable | JsonRpcService_doesNotExist
_action_list_text
=
'''error.handling.missing.callable | JsonRpcService_doesNotExist
...
...
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