diff --git a/exchangelib/services/common.py b/exchangelib/services/common.py index 405f3501..acd0e571 100644 --- a/exchangelib/services/common.py +++ b/exchangelib/services/common.py @@ -590,6 +590,7 @@ def _get_element_container(self, message, name=None): # Raise any non-acceptable errors in the container, or return the container or the acceptable exception instance msg_text = get_xml_attr(message, f"{{{MNS}}}MessageText") msg_xml = message.find(f"{{{MNS}}}MessageXml") + rule_errors = message.find(f"{{{MNS}}}RuleOperationErrors") if response_class == "Warning": try: raise self._get_exception(code=response_code, text=msg_text, msg_xml=msg_xml) @@ -603,12 +604,12 @@ def _get_element_container(self, message, name=None): return container # response_class == 'Error', or 'Success' and not 'NoError' try: - raise self._get_exception(code=response_code, text=msg_text, msg_xml=msg_xml) + raise self._get_exception(code=response_code, text=msg_text, msg_xml=msg_xml, rule_errors=rule_errors) except self.ERRORS_TO_CATCH_IN_RESPONSE as e: return e @staticmethod - def _get_exception(code, text, msg_xml): + def _get_exception(code, text, msg_xml=None, rule_errors=None): """Parse error messages contained in EWS responses and raise as exceptions defined in this package.""" if not code: return TransportError(f"Empty ResponseCode in ResponseMessage (MessageText: {text}, MessageXml: {msg_xml})") @@ -646,6 +647,13 @@ def _get_exception(code, text, msg_xml): except KeyError: # Inner code is unknown to us. Just append to the original text text += f" (inner error: {inner_code}({inner_text!r}))" + if rule_errors is not None: + for rule_error in rule_errors.findall(f"{{{TNS}}}RuleOperationError"): + for error in rule_error.find(f"{{{TNS}}}ValidationErrors").findall(f"{{{TNS}}}Error"): + field_uri = get_xml_attr(error, f"{{{TNS}}}FieldURI") + error_code = get_xml_attr(error, f"{{{TNS}}}ErrorCode") + error_message = get_xml_attr(error, f"{{{TNS}}}ErrorMessage") + text += f" ({error_code} on field {field_uri}: {error_message})" try: # Raise the error corresponding to the ResponseCode return vars(errors)[code](text) diff --git a/exchangelib/services/get_user_settings.py b/exchangelib/services/get_user_settings.py index 3ffe64ad..272bf67d 100644 --- a/exchangelib/services/get_user_settings.py +++ b/exchangelib/services/get_user_settings.py @@ -86,6 +86,6 @@ def _get_element_container(self, message, name=None): return container # Raise any non-acceptable errors in the container, or return the acceptable exception instance try: - raise self._get_exception(code=res.error_code, text=res.error_message, msg_xml=None) + raise self._get_exception(code=res.error_code, text=res.error_message) except self.ERRORS_TO_CATCH_IN_RESPONSE as e: return e diff --git a/tests/test_account.py b/tests/test_account.py index 2b513eb5..3d0b3c99 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -10,6 +10,7 @@ ErrorAccessDenied, ErrorDelegateNoUser, ErrorFolderNotFound, + ErrorInboxRulesValidationError, ErrorInvalidUserSid, ErrorNotDelegate, UnauthorizedError, @@ -384,10 +385,6 @@ def test_all_inbox_rule_actions(self): "move_to_folder": MoveToFolder(distinguished_folder_id=self.account.trash.to_id()), "permanent_delete": True, # Cannot be random. False would be a no-op action "redirect_to_recipients": [Address(email_address=get_random_email())], - # TODO: Throws "UnsupportedRule: The operation on this unsupported rule is not allowed." - # "send_sms_alert_to_recipients": [Address(email_address=get_random_email())], - # TODO: throws "InvalidValue: Id must be non-empty." even though we follow MSDN docs - # "server_reply_with_message": Message(folder=self.account.inbox, subject="Foo").save().to_id(), "stop_processing_rules": True, # Cannot be random. False would be a no-op action }.items(): with self.subTest(action_name=action_name, action=action): @@ -398,3 +395,32 @@ def test_all_inbox_rule_actions(self): actions=Actions(**{action_name: action}), ).save() rule.delete() + + # TODO: Throws "UnsupportedRule: The operation on this unsupported rule is not allowed." + with self.assertRaises(ErrorInboxRulesValidationError) as e: + Rule( + account=self.account, + display_name=get_random_string(16), + priority=get_random_int(), + actions=Actions(send_sms_alert_to_recipients=[Address(email_address=get_random_email())]), + ).save() + self.assertEqual( + e.exception.args[0], + "A validation error occurred while executing the rule operation. (UnsupportedRule on field " + "Action:SendSMSAlertToRecipients: The operation on this unsupported rule is not allowed.)", + ) + # TODO: throws "InvalidValue: Id must be non-empty." even though we follow MSDN docs + with self.assertRaises(ErrorInboxRulesValidationError) as e: + Rule( + account=self.account, + display_name=get_random_string(16), + priority=get_random_int(), + actions=Actions( + server_reply_with_message=Message(folder=self.account.inbox, subject="Foo").save().to_id() + ), + ).save() + self.assertEqual( + e.exception.args[0], + "A validation error occurred while executing the rule operation. (InvalidValue on field " + "Action:ServerReplyWithMessage: Id must be non-empty.)", + )