Skip to content

Commit

Permalink
Merge pull request #293 from GSA/feature/89_API_Error_Handling
Browse files Browse the repository at this point in the history
Improving Error Handling From SAM API
  • Loading branch information
gsa-bri authored Nov 28, 2023
2 parents b187bf1 + 0af974e commit 45a6810
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/fbo_scraper/get_opps.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

logger = logging.getLogger(__name__)

class SamApiError(Exception):
pass

def get_opportunities_search_url(
api_key=None,
Expand Down Expand Up @@ -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'])
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions src/fbo_scraper/main.py
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -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:
Expand Down
26 changes: 26 additions & 0 deletions tests/test_get_opps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down

0 comments on commit 45a6810

Please sign in to comment.