Skip to content
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

Merged
merged 8 commits into from
Jun 8, 2023

Conversation

Konstantinov-Innokentii
Copy link
Member

@Konstantinov-Innokentii Konstantinov-Innokentii commented Jun 1, 2023

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

  • Unit, integration, and e2e (if applicable) tests updated
  • Documentation added (or pr:no public docs PR label added if not required)
  • CHANGELOG.md updated (or pr:no changelog PR label added if not required)

@Konstantinov-Innokentii Konstantinov-Innokentii added pr:no changelog pr:no public docs Added to a PR that does not require public documentation updates labels Jun 1, 2023
@@ -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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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):
Copy link
Contributor

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?

Copy link
Member Author

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

@Konstantinov-Innokentii
Copy link
Member Author

@mderynck could you please add unittests simular to what @joeyorlando did in his PR
Otherwise LGTM, thanks for tackling that issue @mderynck and @joeyorlando !

Comment on lines +113 to +138
"""
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice 👍

Comment on lines +80 to +151
@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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@Konstantinov-Innokentii Konstantinov-Innokentii added this pull request to the merge queue Jun 8, 2023
Merged via the queue into dev with commit c7f8ac3 Jun 8, 2023
@Konstantinov-Innokentii Konstantinov-Innokentii deleted the phone_notificator_granular_errors branch June 8, 2023 06:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr:no public docs Added to a PR that does not require public documentation updates
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants