from typing import Any, List from datadog_api_client import ApiClient, Configuration from datadog_api_client.v1.api.dashboards_api import DashboardsApi from datadog_api_client.v1.model.dashboard_summary import DashboardSummary from datadog_api_client.v1.model.dashboard_summary_definition import DashboardSummaryDefinition from datadog_api_client.v2.api.software_catalog_api import SoftwareCatalogApi from datadog_api_client.v2.api.teams_api import TeamsApi from datadog_api_client.v2.model.entity_data import EntityData from datadog_api_client.v2.model.include_type import IncludeType from datadog_api_client.v2.model.list_entity_catalog_response import ListEntityCatalogResponse from ddtrace import tracer from datadog_tools.models.software_catalog import KindEnum class Datadog: """Provides methods to interact with the Datadog API.""" def __init__(self, api_key: str, app_key: str): configuration = Configuration() configuration.api_key["apiKeyAuth"] = api_key configuration.api_key["appKeyAuth"] = app_key self.client = ApiClient(configuration) def __enter__(self): self.client.__enter__() return self def __exit__(self, type, value, traceback): self.client.__exit__(type, value, traceback) @tracer.wrap(service="datadog-tools", resource="Datadog.get_teams") def get_teams(self): """ Fetches the list of teams from the Datadog API. :return: A list of team handles. """ teams_api = TeamsApi(self.client) teams = [] for team in teams_api.list_teams_with_pagination(): teams.append(team.attributes.handle) return teams @tracer.wrap(service="datadog-tools", resource="Datadog.get_dashboards") def get_dashboards(self) -> List[DashboardSummaryDefinition]: """ Fetches the list of dashboards from the Datadog API. :return: A list of dashboard IDs. """ dashboards_api = DashboardsApi(self.client) response: DashboardSummary = dashboards_api.list_dashboards() return response.dashboards @tracer.wrap(service="datadog-tools", resource="Datadog.upsert_catalog_entity") def upsert_catalog_entity(self, body): """ Upserts a catalog entity in the Datadog Software Catalog. :param body: The body of the request containing the catalog entity data. :return: The response from the API. """ software_catalog_api = SoftwareCatalogApi(self.client) return software_catalog_api.upsert_catalog_entity(body=body) @tracer.wrap(service="datadog-tools", resource="Datadog.get_system") def get_system(self, name) -> EntityData | None: """ Fetches a system entity by name from the Datadog Software Catalog. :param name: The name of the system entity to fetch. :return: The system entity data. """ software_catalog_api = SoftwareCatalogApi(self.client) response: ListEntityCatalogResponse = software_catalog_api.list_catalog_entity(filter_name=name) for entity in response.data: if entity.attributes.kind == "system": return entity return None @tracer.wrap(service="datadog-tools", resource="Datadog.get_entity_schema") def get_entity_schema(self, name: str, kind: KindEnum) -> dict[Any, Any] | None: """ Fetches a schema for an entity with a given name from the Datadog Software Catalog. API response for the schema can be either datadog_api_client.v2.model.entity_v3.EntityV3 or dict, so this method always converts the response to dictionary :param name: The name of the system entity to fetch. :return: The entity schema as a dictionary. """ software_catalog_api = SoftwareCatalogApi(self.client) response: ListEntityCatalogResponse = software_catalog_api.list_catalog_entity( include=IncludeType.SCHEMA, filter_name=name, ) if "included" in response: for entity in response.included: if "schema" in entity.attributes: # Response is inconsistent in terms of the 'schema' # objects uploaded via API have datadog_api_client.v2.model.entity_v3.EntityV3 # objects uploaded via UI have dict instead if type(entity.attributes["schema"]) is dict: if entity.attributes["schema"]["kind"] == kind.value: return entity.attributes["schema"] elif str(entity.attributes["schema"].kind) == kind.value: return entity.attributes["schema"].to_dict() return None @tracer.wrap(service="datadog-tools", resource="Datadog.list_entities") def list_entities(self, kind: KindEnum) -> List[dict[Any, Any]]: """ Returns a list of dictionaries with schemas for all service type objects :param kind: The type of entities to list. :return: List of dictionaries with schemas :rtype: List[dict[Any, Any]] """ software_catalog_api = SoftwareCatalogApi(self.client) paginator = software_catalog_api.list_catalog_entity_with_pagination(filter_kind=kind.value) # For some reason include=IncludeType.SCHEMA doesn't work with paginated calls names = [entity.attributes.name for entity in paginator] schemas = [] for name in names: schema = self.get_entity_schema(name=name, kind=kind) if schema: schemas.append(schema) return schemas