Skip to content

Commit

Permalink
Merge pull request #76 from sanger/Y24-016-handle-502-status-from-nginx
Browse files Browse the repository at this point in the history
Y24-016: Handle 502 status from nginx
  • Loading branch information
sdjmchattie authored Mar 25, 2024
2 parents c8b1edb + 683481b commit d2bad5c
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import logging
from datetime import datetime

from unittest.mock import patch
import pytest
import requests
import requests_mock

from tol_lab_share.messages.traction.qc_message import TractionQcMessage, QcRequestSerializer, TractionQcMessageRequest
from lab_share_lib.exceptions import TransientRabbitError

logger = logging.getLogger(__name__)

@pytest.fixture(autouse=True)
def mock_logger():
with patch("tol_lab_share.messages.traction.qc_message.logger") as logger:
yield logger


class TestTractionQcMessage:
Expand Down Expand Up @@ -125,3 +131,30 @@ def test_qc_request_serializer_clear_empty_value_keys(self):
request = TractionQcMessageRequest()
serial = QcRequestSerializer(request)
assert serial.clear_empty_value_keys({"asdf": "1234", "bcde": ""}) == {"asdf": "1234"}

def test_raises_transient_error_when_traction_api_responds_502(self, valid_traction_qc_message, config):
# This happens when the Traction API is down but nginx is still up.
with requests_mock.Mocker() as m:
m.post(config.TRACTION_URL, text="Error", status_code=502)
with pytest.raises(TransientRabbitError):
valid_traction_qc_message.send(config.TRACTION_URL)

def test_raises_transient_error_when_post_raises(self, valid_traction_qc_message, config, mock_logger):
# This happens when nginx serving Traction API is down.
cause = requests.exceptions.RequestException()

with requests_mock.Mocker() as m:
m.post(config.TRACTION_URL, exc=cause)
with pytest.raises(TransientRabbitError) as raised:
valid_traction_qc_message.send(config.TRACTION_URL)

assert "QcMessage" in raised.value.message
mock_logger.exception.assert_called_once_with(cause)

def test_can_detect_other_errors_on_sent(self, valid_traction_qc_message, config):
with requests_mock.Mocker() as m:
m.post(config.TRACTION_URL, text="Error", status_code=422)
valid_traction_qc_message.send(config.TRACTION_URL)

assert not valid_traction_qc_message.validate()
assert len(valid_traction_qc_message.errors) > 0
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
from unittest.mock import patch
import pytest
import requests
from tol_lab_share.messages.traction.reception_message import TractionReceptionMessage
from lab_share_lib.exceptions import TransientRabbitError
from datetime import datetime
import requests_mock


@pytest.fixture(autouse=True)
def mock_logger():
with patch("tol_lab_share.messages.traction.reception_message.logger") as logger:
yield logger


def valid_traction_message():
instance = TractionReceptionMessage()
request = instance.create_request()
Expand Down Expand Up @@ -768,7 +778,29 @@ def test_can_generate_payload_for_mix_of_plate_and_tubes(self):
}
}

def test_can_detect_errors_on_sent(self, config):
def test_raises_transient_error_when_traction_api_responds_502(self, config):
# This happens when the Traction API is down but nginx is still up.
vt = valid_traction_message()

with requests_mock.Mocker() as m:
m.post(config.TRACTION_URL, text="Error", status_code=502)
with pytest.raises(TransientRabbitError):
vt.send(config.TRACTION_URL)

def test_raises_transient_error_when_post_raises(self, config, mock_logger):
# This happens when nginx serving Traction API is down.
vt = valid_traction_message()
cause = requests.exceptions.RequestException()

with requests_mock.Mocker() as m:
m.post(config.TRACTION_URL, exc=cause)
with pytest.raises(TransientRabbitError) as raised:
vt.send(config.TRACTION_URL)

assert "ReceptionMessage" in raised.value.message
mock_logger.exception.assert_called_once_with(cause)

def test_can_detect_other_errors_on_sent(self, config):
vt = valid_traction_message()

with requests_mock.Mocker() as m:
Expand Down
21 changes: 18 additions & 3 deletions tol_lab_share/messages/traction/qc_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,21 @@ def error_code_traction_problem(self, status_code: int, error_str: str) -> None:
error_codes.ERROR_27_TRACTION_QC_REQUEST_FAILED, text=f"HTTP CODE: { status_code }, MSG: {error_str}"
)

def raise_submission_error(self, cause: Exception | None = None) -> None:
"""Raises an error when submitting the message to Traction with an optional cause exception.
Args:
cause (Exception): The exception that caused the need to raise.
Raises:
TransientRabbitError: Always raised to cause a 30 second delay before trying to process the message again.
"""
logger.critical(f"Error submitting {self.__class__.__name__} to the Traction API.")
if cause:
logger.exception(cause)

raise TransientRabbitError(f"There was an error POSTing the {self.__class__.__name__} to the Traction API.")

def send(self, url: str) -> bool:
"""Sends a request to Traction. If is correct returns true, if not it will trigger an error and
return False
Expand All @@ -206,10 +221,10 @@ def send(self, url: str) -> bool:
try:
r = post(url, headers=headers, data=dumps(self.payload(), default=str), verify=self._validate_certificates)
except Exception as ex:
logger.critical(f"Error submitting {self.__class__.__name__} to the Traction API.")
logger.exception(ex)
self.raise_submission_error(ex)

raise TransientRabbitError(f"There was an error POSTing the {self.__class__.__name__} to the Traction API.")
if r.status_code == codes.bad_gateway:
self.raise_submission_error()

self._sent = r.status_code == codes.created
if not self._sent:
Expand Down
21 changes: 18 additions & 3 deletions tol_lab_share/messages/traction/reception_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,21 @@ def error_code_traction_problem(self, status_code: int, error_str: str) -> None:
error_codes.ERROR_13_TRACTION_REQUEST_FAILED, text=f"HTTP CODE: { status_code }, MSG: {error_str}"
)

def raise_submission_error(self, cause: Exception | None = None) -> None:
"""Raises an error when submitting the message to Traction with an optional cause exception.
Args:
cause (Exception): The exception that caused the need to raise.
Raises:
TransientRabbitError: Always raised to cause a 30 second delay before trying to process the message again.
"""
logger.critical(f"Error submitting {self.__class__.__name__} to the Traction API.")
if cause:
logger.exception(cause)

raise TransientRabbitError(f"There was an error POSTing the {self.__class__.__name__} to the Traction API.")

def send(self, url: str) -> bool:
"""Sends a Traction API request to the provided URL.
If the send fails, an error code will be recorded.
Expand All @@ -324,10 +339,10 @@ def send(self, url: str) -> bool:
try:
r = post(url, headers=headers, data=dumps(self.payload()), verify=self._validate_certificates)
except Exception as ex:
logger.critical(f"Error submitting {self.__class__.__name__} to the Traction API.")
logger.exception(ex)
self.raise_submission_error(ex)

raise TransientRabbitError(f"There was an error POSTing the {self.__class__.__name__} to the Traction API.")
if r.status_code == codes.bad_gateway:
self.raise_submission_error()

self._sent = r.status_code == codes.created
if not self._sent:
Expand Down

0 comments on commit d2bad5c

Please sign in to comment.