-
Notifications
You must be signed in to change notification settings - Fork 291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add BaseFailed exceptions for phone_notificator #2074
Add BaseFailed exceptions for phone_notificator #2074
Conversation
…s' into phone_notificator_granular_errors
@@ -650,3 +647,10 @@ def check_availability(self, request, pk): | |||
user = self.get_object() | |||
warnings = check_user_availability(user=user, team=request.user.current_team) | |||
return Response(data={"warnings": warnings}, status=status.HTTP_200_OK) | |||
|
|||
|
|||
def handle_phone_notificator_failed(exc: BaseFailed) -> Response: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def handle_phone_notificator_failed(exc: BaseFailed) -> Response: | |
def handle_phone_notification_failed(exc: BaseFailed) -> Response: |
graceful_msg: string with some details about exception which can be exposed to caller. | ||
""" | ||
|
||
def __init__(self, graceful_msg=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where is graceful_msg
being set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nowhere yet. but it's expected to be set in concrete providers, me or @mderynck will add this
@mderynck could you please add unittests simular to what @joeyorlando did in his PR |
""" | ||
Errors we will raise without graceful messages: | ||
|
||
20404 - We should not be requesting missing resources | ||
30808 - Unknown error, likely on the carrier side | ||
30007, 32017 - Blocked or filtered, Intermediary / Carrier Analytics blocked call | ||
due to poor reputation score on the telephone number: | ||
* We need to register our number or sender with the analytics provider or carrier for that jurisdiction | ||
""" | ||
|
||
def _get_graceful_msg(self, e, number): | ||
if e.code in (30003, 30005): | ||
return f"Destination handset {number} is unreachable" | ||
elif e.code == 30004: | ||
return f"Sending message to {number} is blocked" | ||
elif e.code == 30006: | ||
return f"Cannot send to {number} is landline or unreachable carrier" | ||
elif e.code == 30410: | ||
return f"Provider for {number} is experiencing timeouts" | ||
elif e.code == 60200: | ||
return f"{number} is incorrectly formatted" | ||
elif e.code in (21215, 60410, 60605): | ||
return f"Verification to {number} is blocked" | ||
elif e.code == 60203: | ||
return f"Max verification attempts for {number} reached" | ||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice 👍
@pytest.mark.django_db | ||
@pytest.mark.parametrize( | ||
"twilio_code,expected_exception,graceful_msg,provider_method,mock_method", | ||
[ | ||
(60200, FailedToMakeCall, True, lambda p: p.make_call(TEST_NUMBER, TEST_MESSAGE), "_call_create"), | ||
(30808, FailedToMakeCall, False, lambda p: p.make_call(TEST_NUMBER, TEST_MESSAGE), "_call_create"), | ||
(None, FailedToMakeCall, False, lambda p: p.make_call(TEST_NUMBER, TEST_MESSAGE), "_call_create"), | ||
(30410, FailedToMakeCall, True, lambda p: p.make_notification_call(TEST_NUMBER, TEST_MESSAGE), "_call_create"), | ||
(30808, FailedToMakeCall, False, lambda p: p.make_notification_call(TEST_NUMBER, TEST_MESSAGE), "_call_create"), | ||
(None, FailedToMakeCall, False, lambda p: p.make_notification_call(TEST_NUMBER, TEST_MESSAGE), "_call_create"), | ||
(30004, FailedToSendSMS, True, lambda p: p.send_sms(TEST_NUMBER, TEST_MESSAGE), "_messages_create"), | ||
(30808, FailedToSendSMS, False, lambda p: p.send_sms(TEST_NUMBER, TEST_MESSAGE), "_messages_create"), | ||
(None, FailedToSendSMS, False, lambda p: p.send_sms(TEST_NUMBER, TEST_MESSAGE), "_messages_create"), | ||
( | ||
30006, | ||
FailedToSendSMS, | ||
True, | ||
lambda p: p.send_notification_sms(TEST_NUMBER, TEST_MESSAGE), | ||
"_messages_create", | ||
), | ||
( | ||
30808, | ||
FailedToSendSMS, | ||
False, | ||
lambda p: p.send_notification_sms(TEST_NUMBER, TEST_MESSAGE), | ||
"_messages_create", | ||
), | ||
( | ||
None, | ||
FailedToSendSMS, | ||
False, | ||
lambda p: p.send_notification_sms(TEST_NUMBER, TEST_MESSAGE), | ||
"_messages_create", | ||
), | ||
( | ||
60203, | ||
FailedToFinishVerification, | ||
True, | ||
lambda p: p.finish_verification(TEST_NUMBER, TEST_CODE), | ||
"_verify_sender", | ||
), | ||
( | ||
30808, | ||
FailedToFinishVerification, | ||
False, | ||
lambda p: p.finish_verification(TEST_NUMBER, TEST_CODE), | ||
"_verify_sender", | ||
), | ||
( | ||
None, | ||
FailedToFinishVerification, | ||
False, | ||
lambda p: p.finish_verification(TEST_NUMBER, TEST_CODE), | ||
"_verify_sender", | ||
), | ||
], | ||
) | ||
@mock.patch("apps.twilioapp.phone_provider.TwilioPhoneProvider._normalize_phone_number", return_value=(TEST_NUMBER, 1)) | ||
def test_twilio_provider_exceptions( | ||
mocked_normalize, twilio_code, expected_exception, graceful_msg, provider_method, mock_method | ||
): | ||
provider = TwilioPhoneProvider() | ||
|
||
with mock.patch(f"apps.twilioapp.phone_provider.TwilioPhoneProvider.{mock_method}") as twilio_mock: | ||
twilio_mock.side_effect = TwilioRestException(500, "", code=twilio_code) | ||
with pytest.raises(expected_exception) as exc: | ||
provider_method(provider) | ||
if graceful_msg: | ||
assert len(exc.value.graceful_msg) > 0 | ||
else: | ||
assert exc.value.graceful_msg is None | ||
twilio_mock.assert_called_once() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
What this PR does
Introduces BaseFailed exception for phone_notificator.
Why
We need to somehow distinguish errors we want to be notified - like network errors or invalid twilio credentials (I will call them "real" errors) and errors we want to share with user, but don't want to be paged ( I will call them "fake" errors).
To do that I added "graceful_msg" to all Failed... exceptions. If details field is present - it mean we can return 400 code with the message, if not - 500 code. So, "real" errors will raise Failed... exception, while "fake" will add "graceful_msg".
TODO
handle exceptions handled here #2065
Checklist
pr:no public docs
PR label added if not required)CHANGELOG.md
updated (orpr:no changelog
PR label added if not required)