Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def test_call_actions_raises_exception_on_action_error(self):
"""Client.call_actions raises CallActionError when any action response is an error."""
action_request = [
{
'action': 'action_1',
'body': {'foo': 'bar'},
},
{
'action': 'action_2',
'body': {},
},
] # type: List[Dict[six.text_type, Any]]
error_expected = Error(code=ERROR_CODE_INVALID, message='Invalid input', field='foo')
self.client_settings[SERVICE_NAME]['transport']['kwargs']['action_map']['action_1'] = {
'errors': [error_expected],
}
client = Client(self.client_settings)
for actions in (action_request, [ActionRequest(**a) for a in action_request]):
with self.assertRaises(Client.CallActionError) as e:
client.call_actions(SERVICE_NAME, actions) # type: ignore
self.assertEqual(len(e.exception.actions), 1)
self.assertEqual(e.exception.actions[0].action, 'action_1')
error_response = e.exception.actions[0].errors
self.assertEqual(len(error_response), 1)
self.assertEqual(error_response[0].code, error_expected.code)
self.assertEqual(error_response[0].message, error_expected.message)
self.assertEqual(error_response[0].field, error_expected.field)
{'service_name': 'service_1', 'actions': [{'action': 'action_1'}, {'action': 'action_2'}]},
{'service_name': 'error_service', 'actions': [{'action': 'job_error'}]},
{'service_name': 'service_2', 'actions': [{'action': 'action_3'}]},
{'service_name': 'service_2', 'actions': [{'action': 'action_4'}]},
],
raise_job_errors=False,
)
self.assertIsNotNone(job_responses)
self.assertEqual(4, len(job_responses))
self.assertEqual(2, len(job_responses[0].actions))
self.assertEqual({'foo': 'bar'}, job_responses[0].actions[0].body)
self.assertEqual({'baz': 3}, job_responses[0].actions[1].body)
self.assertEqual(0, len(job_responses[1].actions))
self.assertEqual([Error(code='BAD_JOB', message='You are a bad job')], job_responses[1].errors)
self.assertEqual(1, len(job_responses[2].actions))
self.assertEqual({'cat': 'dog'}, job_responses[2].actions[0].body)
self.assertEqual(1, len(job_responses[3].actions))
self.assertEqual({'selected': True, 'count': 7}, job_responses[3].actions[0].body)
def test_call_actions_raises_exception_on_job_error(self):
"""Client.call_actions raises Client.JobError when a JobError occurs on the server."""
errors = [Error(code=ERROR_CODE_SERVER_ERROR, message='Something went wrong!')]
with mock.patch(
'pysoa.server.server.Server.execute_job',
new=mock.Mock(side_effect=JobError(errors)),
):
client = Client(self.client_settings)
with self.assertRaises(Client.JobError) as e:
client.call_action(SERVICE_NAME, 'action_1')
self.assertEqual(e.exception.errors, errors)
def a(*_, **__):
raise JobError(errors=[Error(code='BAD_JOB', message='You are a bad job')])
'error_service',
[
ActionRequest(action='okay_action'),
ActionRequest(action='job_error'),
ActionRequest(action='okay_action'),
],
timeout=2,
raise_job_errors=False,
)
self.assertIsNotNone(action_responses)
action_responses_list = list(action_responses)
self.assertEqual(3, len(action_responses_list))
self.assertEqual({'no_error': True}, action_responses_list[0].body)
self.assertEqual([Error(code='BAD_JOB', message='You are a bad job')], action_responses_list[1])
self.assertEqual({'no_error': True}, action_responses_list[2].body)
def process_job(self, job_request): # type: (Dict[six.text_type, Any]) -> JobResponse
"""
Validate, execute, and run the job request, wrapping it with any applicable job middleware.
:param job_request: The job request dict
:return: A `JobResponse` object
"""
try:
# Validate JobRequest message
validation_errors = [
Error(
code=error.code,
message=error.message,
field=error.pointer,
is_caller_error=False, # because this only happens if the client library code is buggy
)
for error in (JobRequestSchema.errors(job_request) or [])
]
if validation_errors:
raise JobError(errors=validation_errors, set_is_caller_error_to=None)
# Add the client object in case a middleware or action wishes to use it
job_request['client'] = self.make_client(job_request['context'])
# Add the run_coroutine in case a middleware or action wishes to use it
if self._async_event_loop_thread:
job_request['run_coroutine'] = self._async_event_loop_thread.run_coroutine
def _get_response_for_single_action(self, request_action_name): # type: (six.text_type) -> Dict[six.text_type, Any]
action_name = request_action_name
switch = None # type: Optional[SupportsInt]
match = SWITCHED_ACTION_RE.match(action_name)
if match:
action_name = match.group(str('action'))
if match.group(str('default')):
switch = SwitchedAction.DEFAULT_ACTION
else:
switch = int(match.group(str('switch')))
if action_name not in self.server.action_class_map and action_name not in ('status', 'introspect'):
raise ActionError(
errors=[Error(
code=ERROR_CODE_INVALID,
message='Action not defined in service',
field='action_name',
is_caller_error=True,
)],
set_is_caller_error_to=None,
)
if action_name in self.server.action_class_map:
action_class = self.server.action_class_map[action_name]
if isinstance(action_class, type) and issubclass(action_class, SwitchedAction):
if switch:
if switch == SwitchedAction.DEFAULT_ACTION:
action_class = action_class.switch_to_action_map[-1][1]
else:
for matching_switch, action_class in action_class.switch_to_action_map:
:param body: The body to send to the action
:param raise_action_errors: If `True` (the default), all action errors will be raised; otherwise, an
`ActionResponse` containing the errors will be returned.
:param is_caller_error: If `True` (defaults to `False`), raised action errors will be marked as the
responsibility of the caller. Action errors are usually the responsibility of the
caller, but the default here is the opposite since the responsibility usually lies in
the service that is calling itself and should know better.
:return: the action response.
:raises: ActionError
"""
server = getattr(self, '_server', None)
if not server:
# This is never a caller error, because it can only happen due to a bug in PySOA or the service.
errors = [Error(
code=ERROR_CODE_SERVER_ERROR,
message="No `_server` attribute set on action request object (and can't make request without it)",
is_caller_error=False,
)]
if raise_action_errors:
raise ActionError(errors, set_is_caller_error_to=None)
return ActionResponse(action=action, errors=errors)
if action not in server.action_class_map:
# This is never a caller error, because it can only happen due to a bug in the service calling itself.
errors = [Error(
code=ERROR_CODE_UNKNOWN,
message='The action "{}" was not found on this server.'.format(action),
field='action',
is_caller_error=False,
)]
def _replace_errors_if_necessary(errors, is_caller_error):
# type: (Iterable[Error], bool) -> List[Error]
new_errors = []
for e in errors:
if e.is_caller_error == is_caller_error:
new_errors.append(e)
else:
# Error is immutable, so return a new one
new_errors.append(Error(
code=e.code,
message=e.message,
field=e.field,
traceback=e.traceback,
variables=e.variables,
denied_permissions=e.denied_permissions,
is_caller_error=is_caller_error,
))
return new_errors