diff --git a/integrations/dynatrace/.port/resources/port-app-config.yaml b/integrations/dynatrace/.port/resources/port-app-config.yaml index a461e44d83..d8ece6bf47 100644 --- a/integrations/dynatrace/.port/resources/port-app-config.yaml +++ b/integrations/dynatrace/.port/resources/port-app-config.yaml @@ -5,6 +5,7 @@ resources: selector: query: "true" entityTypes: ["APPLICATION", "SERVICE"] + entityFields: "firstSeenTms,lastSeenTms,tags" port: entity: mappings: diff --git a/integrations/dynatrace/CHANGELOG.md b/integrations/dynatrace/CHANGELOG.md index 34cfca6c86..f3162b4213 100644 --- a/integrations/dynatrace/CHANGELOG.md +++ b/integrations/dynatrace/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +# Port_Ocean 0.1.27 (2024-06-25) + +### Features + +- Fields included in response for Entity Types can now be configured (0.1.27) + +### Bug Fixes + +- Fixed bug causing missing fields in entities (0.1.27) + + # Port_Ocean 0.1.26 (2024-06-23) ### Improvements diff --git a/integrations/dynatrace/client.py b/integrations/dynatrace/client.py index f585839e9e..f98a5c9119 100644 --- a/integrations/dynatrace/client.py +++ b/integrations/dynatrace/client.py @@ -7,7 +7,7 @@ from port_ocean.context.event import event from port_ocean.utils import http_async_client -from integration import DynatraceResourceConfig +from integration import DynatraceResourceConfig, EntityFieldsType class ResourceKey(StrEnum): @@ -61,12 +61,18 @@ async def get_slos(self) -> AsyncGenerator[list[dict[str, Any]], None]: yield slos async def _get_entities_from_type( - self, type_: str + self, type_: str, entity_fields: EntityFieldsType | None ) -> AsyncGenerator[list[dict[str, Any]], None]: + params = { + "entitySelector": f'type("{type_}")', + "pageSize": 100, + } + if entity_fields: + params["fields"] = entity_fields async for entities in self._get_paginated_resources( f"{self.host_url}/entities", "entities", - params={"entitySelector": f'type("{type_}")', "pageSize": 100}, + params=params, ): yield entities @@ -74,7 +80,9 @@ async def get_entities(self) -> AsyncGenerator[list[dict[str, Any]], None]: selector = typing.cast(DynatraceResourceConfig, event.resource_config).selector for entity_type in selector.entity_types: - async for entities in self._get_entities_from_type(entity_type): + async for entities in self._get_entities_from_type( + entity_type, selector.entity_fields + ): yield entities async def get_single_problem(self, problem_id: str) -> dict[str, Any]: diff --git a/integrations/dynatrace/integration.py b/integrations/dynatrace/integration.py index 6381c971c9..5b08a7ef1e 100644 --- a/integrations/dynatrace/integration.py +++ b/integrations/dynatrace/integration.py @@ -1,3 +1,6 @@ +import re +from typing import Literal + from port_ocean.core.handlers.port_app_config.api import APIPortAppConfig from port_ocean.core.handlers.port_app_config.models import ( PortAppConfig, @@ -8,6 +11,25 @@ from pydantic.fields import Field +class EntityFieldsType(str): + @classmethod + def validate(cls, value: str) -> None: + # Regular expression to validate the format of the aggregation value + regex = ( + r"^(\+?(firstSeenTms|lastSeenTms|tags|fromRelationships|icon" + r"|managementZones|properties|toRelationships|properties\.\d+)" + r"(,\+?(firstSeenTms|lastSeenTms|tags|fromRelationships|icon|" + r"managementZones|properties|toRelationships|properties\.\w+))*)*$" + ) + if not re.match(regex, value): + raise ValueError( + "Invalid entity field format. Use 'firstSeenTms', 'lastSeenTms', 'tags', " + "'fronRelationships', 'icon', 'managementZones', 'properties', " + "'toRelationships', 'properties.FIELD' or comma-separated list" + " of specified values. Values can be prefixed with '+'." + ) + + class DynatraceEntitySelector(Selector): entity_types: list[str] = Field( default=["APPLICATION", "SERVICE"], @@ -15,9 +37,14 @@ class DynatraceEntitySelector(Selector): alias="entityTypes", ) + entity_fields: EntityFieldsType | None = Field( + description="List of fields to include in each entity", alias="entityFields" + ) + class DynatraceResourceConfig(ResourceConfig): selector: DynatraceEntitySelector + kind: Literal["entity"] class DynatracePortAppConfig(PortAppConfig): diff --git a/integrations/dynatrace/pyproject.toml b/integrations/dynatrace/pyproject.toml index d642caf5a4..99b6acc70f 100644 --- a/integrations/dynatrace/pyproject.toml +++ b/integrations/dynatrace/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dynatrace" -version = "0.1.26" +version = "0.1.27" description = "An integration used to import Dynatrace resources into Port" authors = ["Ayodeji Adeoti <>"]