Skip to content

Commit

Permalink
fix: improve error messages for inbox rule validation errors
Browse files Browse the repository at this point in the history
  • Loading branch information
ecederstrand committed Apr 11, 2024
1 parent ff43417 commit cc45183
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 7 deletions.
12 changes: 10 additions & 2 deletions exchangelib/services/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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})")
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion exchangelib/services/get_user_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
34 changes: 30 additions & 4 deletions tests/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ErrorAccessDenied,
ErrorDelegateNoUser,
ErrorFolderNotFound,
ErrorInboxRulesValidationError,
ErrorInvalidUserSid,
ErrorNotDelegate,
UnauthorizedError,
Expand Down Expand Up @@ -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):
Expand All @@ -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.)",
)

0 comments on commit cc45183

Please sign in to comment.