mirror of
https://github.com/GSA/notifications-api.git
synced 2026-07-18 13:40:31 -04:00
In response to: [^1].
The stacktrace conveys the same and more information. We don't do
anything different for each exception class, so there's no value
in having three of them over one exception.
I did think about DRYing-up the duplicate exception behaviour into
the base class one. This isn't ideal because the base class would
be making assumptions about how inheriting classes make requests,
which might change with future providers. Although it might be nice
to have more info in the top-level message, we'll still get it in
the stacktrace e.g.
ValueError: Expected 'code' to be '0'
During handling of the above exception, another exception occurred:
app.clients.sms.SmsClientResponseException: SMS client error (Invalid response JSON)
requests.exceptions.ReadTimeout
During handling of the above exception, another exception occurred:
app.clients.sms.SmsClientResponseException: SMS client error (Request failed)
[^1]: https://github.com/alphagov/notifications-api/pull/3493#discussion_r837363717
111 lines
3.6 KiB
Python
111 lines
3.6 KiB
Python
import json
|
|
|
|
from requests import RequestException, request
|
|
|
|
from app.clients.sms import SmsClient, SmsClientResponseException
|
|
|
|
mmg_response_map = {
|
|
'2': {'status': 'permanent-failure', 'substatus': {
|
|
"1": "Number does not exist",
|
|
"4": "Rejected by operator",
|
|
"5": "Unidentified Subscriber",
|
|
"9": "Undelivered",
|
|
"11": "Service for Subscriber suspended",
|
|
"12": "Illegal equipment",
|
|
"2049": "Subscriber IMSI blacklisted",
|
|
"2050": "Number blacklisted in do-not-disturb blacklist",
|
|
"2052": "Destination number blacklisted",
|
|
"2053": "Source address blacklisted"
|
|
}},
|
|
'3': {'status': 'delivered', 'substatus': {"2": "Delivered to operator", "5": "Delivered to handset"}},
|
|
'4': {'status': 'temporary-failure', 'substatus': {
|
|
"6": "Absent Subscriber",
|
|
"8": "Roaming not allowed",
|
|
"13": "SMS Not Supported",
|
|
"15": "Expired",
|
|
"27": "Absent Subscriber",
|
|
"29": "Invalid delivery report",
|
|
"32": "Delivery Failure",
|
|
}},
|
|
'5': {'status': 'permanent-failure', 'substatus': {
|
|
"6": "Network out of coverage",
|
|
"8": "Incorrect number prefix",
|
|
"10": "Number on do-not-disturb service",
|
|
"11": "Sender id not registered",
|
|
"13": "Sender id blacklisted",
|
|
"14": "Destination number blacklisted",
|
|
"19": "Routing unavailable",
|
|
"20": "Rejected by anti-flooding mechanism",
|
|
"21": "System error", # it says to retry those messages or contact support
|
|
"23": "Duplicate message id",
|
|
"24": "Message formatted incorrectly",
|
|
"25": "Message too long",
|
|
"51": "Missing recipient value",
|
|
"52": "Invalid destination",
|
|
}},
|
|
}
|
|
|
|
|
|
def get_mmg_responses(status, detailed_status_code=None):
|
|
return (mmg_response_map[status]["status"], mmg_response_map[status]["substatus"].get(detailed_status_code, None))
|
|
|
|
|
|
class MMGClientResponseException(SmsClientResponseException):
|
|
def __init__(self, response, exception):
|
|
status_code = response.status_code if response is not None else 504
|
|
text = response.text if response is not None else "Gateway Time-out"
|
|
|
|
self.status_code = status_code
|
|
self.text = text
|
|
self.exception = exception
|
|
|
|
def __str__(self):
|
|
return "Code {} text {} exception {}".format(self.status_code, self.text, str(self.exception))
|
|
|
|
|
|
class MMGClient(SmsClient):
|
|
'''
|
|
MMG sms client
|
|
'''
|
|
|
|
def init_app(self, *args, **kwargs):
|
|
super().init_app(*args, **kwargs)
|
|
self.api_key = self.current_app.config.get('MMG_API_KEY')
|
|
self.mmg_url = self.current_app.config.get('MMG_URL')
|
|
|
|
@property
|
|
def name(self):
|
|
return 'mmg'
|
|
|
|
def try_send_sms(self, to, content, reference, international, sender):
|
|
data = {
|
|
"reqType": "BULK",
|
|
"MSISDN": to,
|
|
"msg": content,
|
|
"sender": sender,
|
|
"cid": reference,
|
|
"multi": True
|
|
}
|
|
|
|
try:
|
|
response = request(
|
|
"POST",
|
|
self.mmg_url,
|
|
data=json.dumps(data),
|
|
headers={
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Basic {}'.format(self.api_key)
|
|
},
|
|
timeout=60
|
|
)
|
|
|
|
response.raise_for_status()
|
|
try:
|
|
json.loads(response.text)
|
|
except (ValueError, AttributeError):
|
|
raise SmsClientResponseException("Invalid response JSON")
|
|
except RequestException:
|
|
raise SmsClientResponseException("Request failed")
|
|
|
|
return response
|