From bad967374505720c3e3b349f864c01b83e05d075 Mon Sep 17 00:00:00 2001 From: Adam Buckingham Date: Mon, 27 Nov 2023 12:13:27 -0500 Subject: [PATCH 1/2] Unit Test for Error handling the API response --- tests/test_get_opps.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_get_opps.py b/tests/test_get_opps.py index dc5e715..57258e4 100644 --- a/tests/test_get_opps.py +++ b/tests/test_get_opps.py @@ -68,6 +68,7 @@ def test_get_opps_for_day(self, mock_session, mock_search_url): } mock_response = MagicMock() mock_response.json.return_value = mock_response_data + mock_response.status_code = 200 mock_session.return_value.get.return_value = mock_response # Call the function with mock data @@ -77,6 +78,31 @@ def test_get_opps_for_day(self, mock_session, mock_search_url): self.assertEqual(len(opps), 1) self.assertEqual(opps[0]['solicitationNumber'], 'ABC123') + @patch('fbo_scraper.get_opps.get_opportunities_search_url', return_value = 'https://api.sam.gov/prod/opportunity/v1/api/search') + @patch('fbo_scraper.get_opps.requests_retry_session') + def test_get_opps_for_day_error(self, mock_session, mock_search_url): + from fbo_scraper.get_opps import SamApiError + mock_search_url.return_value = 'https://api.sam.gov/prod/opportunity/v1/api/search' + + # Set up mock response data + mock_response_data = { + "error": { + "code": "API_KEY_INVALID", + "message": "An invalid API key was supplied. Please submit with a valid API key." + } + } + mock_response = MagicMock() + mock_response.json.return_value = mock_response_data + mock_response.status_code = 403 + mock_session.return_value.get.return_value = mock_response + + # Call the function with mock data + with self.assertRaises(SamApiError) as context: + opps = get_opps_for_day(limit=1) + + # Check the results + self.assertTrue('Sam.gov API returned error message' in str(context.exception)) + @patch('fbo_scraper.get_opps.make_attachement_request', return_value = MagicMock(headers={'Content-Disposition': 'attachment; filename=test.pdf'})) @patch('fbo_scraper.get_opps.shutil.copyfileobj') @requests_mock.Mocker() From 0af974e389c3a06d09d0526c9efef8a9a688686f Mon Sep 17 00:00:00 2001 From: Adam Buckingham Date: Mon, 27 Nov 2023 12:13:46 -0500 Subject: [PATCH 2/2] Creating new exception and logging of error --- src/fbo_scraper/get_opps.py | 11 ++++++++++- src/fbo_scraper/main.py | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/fbo_scraper/get_opps.py b/src/fbo_scraper/get_opps.py index 2c6ce74..29ba9de 100644 --- a/src/fbo_scraper/get_opps.py +++ b/src/fbo_scraper/get_opps.py @@ -17,6 +17,8 @@ logger = logging.getLogger(__name__) +class SamApiError(Exception): + pass def get_opportunities_search_url( api_key=None, @@ -146,6 +148,11 @@ def get_opps_for_day( uri_with_offset = f"{uri}&offset={offset}" r = session.get(uri_with_offset, timeout=100) data = r.json() + + if r.status_code != 200: + api_error = data.get("error", {}).get("message") + raise SamApiError(f"Sam.gov API returned error message: {api_error}") + logger.debug(f"Retrieved json from {uri_with_offset}: {data}") totalRecords = data['totalRecords'] offset += len(data['opportunitiesData']) @@ -333,7 +340,9 @@ def main( transformed_opps = transform_opps( opps, out_path, skip_attachments=skip_attachments ) - + + except SamApiError: + raise except Exception as e: logger.critical(f"Exception {e} getting solicitations", exc_info=True) sys.exit(1) diff --git a/src/fbo_scraper/main.py b/src/fbo_scraper/main.py index c1142ce..52e97d7 100644 --- a/src/fbo_scraper/main.py +++ b/src/fbo_scraper/main.py @@ -1,6 +1,7 @@ import logging from pathlib import Path from fbo_scraper import get_opps +from fbo_scraper.get_opps import SamApiError from fbo_scraper.binaries import binary_path from fbo_scraper.predict import Predict, PredictException from fbo_scraper.db.db_utils import ( @@ -181,6 +182,9 @@ def main( update_old_solicitations(session, max_tests=10) logger.info("Run complete without major errors.") + + except SamApiError as e: + logger.error(f"API Error - {e}", exc_info=True) except PredictException as e: logger.error(f"Prediction Error - {e}", exc_info=True) except DALException as e: