from typing import Dict import backoff import requests from ddtrace import tracer _MAX_TRIES = 5 def _on_sentry_backoff(details): project_slug = details["args"][1] print( f"Sentry request failed for project '{project_slug}'" f" (attempt {details['tries']}/{_MAX_TRIES}), retrying in {details['wait']:.1f}s: {details['exception']}" ) def _on_sentry_giveup(details): project_slug = details["args"][1] print( f"Warning: Sentry unavailable for project '{project_slug}' after {details['tries']} attempts: proceeding " "without Sentry data. Note that Software Catalog will temporarily be missing the Sentry project URL." ) class Sentry: def __init__(self, api_token: str, organization: str): self.api_token = api_token self.organization = organization self.base_url = "https://sentry.io/api/0" self.headers = { "Authorization": f"Bearer {self.api_token}", "Content-Type": "application/json", } @tracer.wrap(service="datadog-tools", resource="Sentry.get_project") @backoff.on_exception( backoff.expo, requests.exceptions.RequestException, max_tries=_MAX_TRIES, on_backoff=_on_sentry_backoff, on_giveup=_on_sentry_giveup, raise_on_giveup=False, ) def get_project(self, project_slug) -> Dict | None: """Retrieve a specific Sentry project by its slug.""" url = f"{self.base_url}/projects/{self.organization}/{project_slug}/" response = requests.get(url, headers=self.headers, timeout=30) if response.status_code == 404: return None response.raise_for_status() return response.json()