Added a 60 second timeout to the MMG and fire text clients.

- This is a CONNECT and a READ timeout.
- Gets wrapped in the standard client exception, with a status code of 504, message Gateway timeout.
- Is quiet noisy in logs to allow us to see it
- Ensures we flick across the provider.

To test change the timeout to 0 and it will timeout.
This commit is contained in:
Martyn Inglis
2017-04-05 15:31:37 +01:00
parent 57bd6494e1
commit 72ecca4dab
4 changed files with 62 additions and 12 deletions

View File

@@ -42,8 +42,10 @@ def get_firetext_responses(status):
class FiretextClientResponseException(SmsClientResponseException): class FiretextClientResponseException(SmsClientResponseException):
def __init__(self, response, exception): def __init__(self, response, exception):
self.status_code = response.status_code status_code = response.status_code if response is not None else 504
self.text = response.text text = response.text if response is not None else "Gateway Time-out"
self.status_code = status_code
self.text = text
self.exception = exception self.exception = exception
def __str__(self): def __str__(self):
@@ -68,11 +70,13 @@ class FiretextClient(SmsClient):
return self.name return self.name
def record_outcome(self, success, response): def record_outcome(self, success, response):
status_code = response.status_code if response else 503
log_message = "API {} request {} on {} response status_code {}".format( log_message = "API {} request {} on {} response status_code {}".format(
"POST", "POST",
"succeeded" if success else "failed", "succeeded" if success else "failed",
self.url, self.url,
response.status_code status_code
) )
if success: if success:

View File

@@ -1,7 +1,6 @@
import json import json
from monotonic import monotonic from monotonic import monotonic
from requests import (request, RequestException) from requests import (request, RequestException)
from requests.exceptions import HTTPError
from app.clients import (STATISTICS_DELIVERED, STATISTICS_FAILURE) from app.clients import (STATISTICS_DELIVERED, STATISTICS_FAILURE)
from app.clients.sms import (SmsClient, SmsClientResponseException) from app.clients.sms import (SmsClient, SmsClientResponseException)
@@ -45,8 +44,11 @@ def get_mmg_responses(status):
class MMGClientResponseException(SmsClientResponseException): class MMGClientResponseException(SmsClientResponseException):
def __init__(self, response, exception): def __init__(self, response, exception):
self.status_code = response.status_code status_code = response.status_code if response is not None else 504
self.text = response.text text = response.text if response is not None else "Gateway Time-out"
self.status_code = status_code
self.text = text
self.exception = exception self.exception = exception
def __str__(self): def __str__(self):
@@ -68,11 +70,12 @@ class MMGClient(SmsClient):
self.mmg_url = current_app.config.get('MMG_URL') self.mmg_url = current_app.config.get('MMG_URL')
def record_outcome(self, success, response): def record_outcome(self, success, response):
status_code = response.status_code if response else 503
log_message = "API {} request {} on {} response status_code {}".format( log_message = "API {} request {} on {} response status_code {}".format(
"POST", "POST",
"succeeded" if success else "failed", "succeeded" if success else "failed",
self.mmg_url, self.mmg_url,
response.status_code status_code
) )
if success: if success:
@@ -118,9 +121,6 @@ class MMGClient(SmsClient):
except RequestException as e: except RequestException as e:
self.record_outcome(False, e.response) self.record_outcome(False, e.response)
raise MMGClientResponseException(response=e.response, exception=e) raise MMGClientResponseException(response=e.response, exception=e)
except HTTPError as e:
self.record_outcome(False, e.response)
raise MMGClientResponseException(response=e.response, exception=e)
finally: finally:
elapsed_time = monotonic() - start_time elapsed_time = monotonic() - start_time
self.statsd_client.timing("clients.mmg.request-time", elapsed_time) self.statsd_client.timing("clients.mmg.request-time", elapsed_time)

View File

@@ -1,10 +1,11 @@
from requests import HTTPError from requests import HTTPError
from urllib.parse import parse_qs from urllib.parse import parse_qs
from requests.exceptions import ConnectTimeout, ReadTimeout
import pytest import pytest
import requests_mock import requests_mock
from app.clients.sms.firetext import get_firetext_responses, SmsClientResponseException from app.clients.sms.firetext import get_firetext_responses, SmsClientResponseException, FiretextClientResponseException
def test_should_return_correct_details_for_delivery(): def test_should_return_correct_details_for_delivery():
@@ -126,3 +127,25 @@ def test_send_sms_override_configured_shortcode_with_sender(mocker, mock_firetex
request_args = parse_qs(request_mock.request_history[0].text) request_args = parse_qs(request_mock.request_history[0].text)
assert request_args['from'][0] == 'fromservice' assert request_args['from'][0] == 'fromservice'
def test_send_sms_raises_if_firetext_rejects_with_timeout(rmock, mock_firetext_client):
to = content = reference = 'foo'
with pytest.raises(FiretextClientResponseException) as exc:
rmock.register_uri('POST', 'https://www.firetext.co.uk/api/sendsms/json', exc=ConnectTimeout)
mock_firetext_client.send_sms(to, content, reference)
assert exc.value.status_code == 504
assert exc.value.text == 'Gateway Time-out'
def test_send_sms_raises_if_firetext_rejects_with_timeout(rmock, mock_firetext_client):
to = content = reference = 'foo'
with pytest.raises(FiretextClientResponseException) as exc:
rmock.register_uri('POST', 'https://www.firetext.co.uk/api/sendsms/json', exc=ReadTimeout)
mock_firetext_client.send_sms(to, content, reference)
assert exc.value.status_code == 504
assert exc.value.text == 'Gateway Time-out'

View File

@@ -3,11 +3,12 @@ import json
import pytest import pytest
import requests_mock import requests_mock
from requests import HTTPError from requests import HTTPError
from requests.exceptions import ConnectTimeout, ReadTimeout
from app import mmg_client from app import mmg_client
from app.clients.sms import SmsClientResponseException from app.clients.sms import SmsClientResponseException
from app.clients.sms.mmg import get_mmg_responses from app.clients.sms.mmg import get_mmg_responses, MMGClientResponseException
def test_should_return_correct_details_for_delivery(): def test_should_return_correct_details_for_delivery():
@@ -113,3 +114,25 @@ def test_send_sms_raises_if_mmg_fails_to_return_json(notify_api, mocker):
assert 'app.clients.sms.mmg.MMGClientResponseException: Code 200 text NOT AT ALL VALID JSON {"key" : "value"}} exception Expecting value: line 1 column 1 (char 0)' in str(exc) # noqa assert 'app.clients.sms.mmg.MMGClientResponseException: Code 200 text NOT AT ALL VALID JSON {"key" : "value"}} exception Expecting value: line 1 column 1 (char 0)' in str(exc) # noqa
assert exc.value.status_code == 200 assert exc.value.status_code == 200
assert exc.value.text == 'NOT AT ALL VALID JSON {"key" : "value"}}' assert exc.value.text == 'NOT AT ALL VALID JSON {"key" : "value"}}'
def test_send_sms_raises_if_mmg_rejects_with_timeout(rmock):
to = content = reference = 'foo'
with pytest.raises(MMGClientResponseException) as exc:
rmock.register_uri('POST', 'https://api.mmg.co.uk/json/api.php', exc=ConnectTimeout)
mmg_client.send_sms(to, content, reference)
assert exc.value.status_code == 504
assert exc.value.text == 'Gateway Time-out'
def test_send_sms_raises_if_firetext_rejects_with_timeout(rmock):
to = content = reference = 'foo'
with pytest.raises(MMGClientResponseException) as exc:
rmock.register_uri('POST', 'https://api.mmg.co.uk/json/api.php', exc=ReadTimeout)
mmg_client.send_sms(to, content, reference)
assert exc.value.status_code == 504
assert exc.value.text == 'Gateway Time-out'