Skip to content

Commit

Permalink
[Integration][PagerDuty] Add support for service analytics (#384)
Browse files Browse the repository at this point in the history
  • Loading branch information
PeyGis authored Feb 13, 2024
1 parent feaa866 commit e6fb139
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 5 deletions.
12 changes: 12 additions & 0 deletions integrations/pagerduty/.port/resources/blueprints.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
"type": "string",
"format": "user"
}
},
"meanSecondsToResolve": {
"title": "Mean Seconds to Resolve",
"type": "number"
},
"meanSecondsToFirstAck": {
"title": "Mean Seconds to First Acknowledge",
"type": "number"
},
"meanSecondsToEngage": {
"title": "Mean Seconds to Engage",
"type": "number"
}
},
"required": []
Expand Down
5 changes: 5 additions & 0 deletions integrations/pagerduty/.port/resources/port-app-config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
createMissingRelatedEntities: true
deleteDependentEntities: true
resources:
- kind: services
selector:
Expand All @@ -12,6 +14,9 @@ resources:
status: .status
url: .html_url
oncall: '[.__oncall_user[].user.email]'
meanSecondsToResolve: .__analytics.mean_seconds_to_resolve
meanSecondsToFirstAck: .__analytics.mean_seconds_to_first_ack
meanSecondsToEngage: .__analytics.mean_seconds_to_engage
- kind: incidents
selector:
query: 'true'
Expand Down
8 changes: 8 additions & 0 deletions integrations/pagerduty/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

<!-- towncrier release notes start -->

# Port_Ocean 0.1.35 (2024-02-12)

### Features

- Improved on the PagerDuty service blueprint by adding analytics data (PORT-6598)


# Port_Ocean 0.1.34 (2024-01-26)

### Features
Expand Down
38 changes: 38 additions & 0 deletions integrations/pagerduty/clients/pagerduty.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from loguru import logger

from port_ocean.utils import http_async_client
from .utils import get_date_range_for_last_n_months


class PagerDutyClient:
Expand Down Expand Up @@ -215,3 +216,40 @@ async def get_incident_analytics(self, incident_id: str) -> dict[str, Any]:
except httpx.HTTPError as e:
logger.error(f"HTTP occurred while fetching incident analytics data: {e}")
return {}

async def get_service_analytics(
self, service_id: str, months_period: int = 3
) -> dict[str, Any]:
logger.info(f"Fetching analytics for service: {service_id}")
url = f"{self.api_url}/analytics/metrics/incidents/services"
date_ranges = get_date_range_for_last_n_months(months_period)

try:
body = {
"filters": {
"service_ids": [service_id],
"created_at_start": date_ranges[0],
"created_at_end": date_ranges[1],
}
}
response = await self.http_client.post(url, json=body)
response.raise_for_status()

logger.info(f"Successfully fetched analytics for service: {service_id}")

return response.json()["data"][0] if response.json()["data"] else {}

except httpx.HTTPStatusError as e:
if e.response.status_code == 404:
logger.debug(
f"Service {service_id} analytics data was not found, skipping..."
)
return {}

logger.error(
f"HTTP error with status code: {e.response.status_code} and response text: {e.response.text}"
)
return {}
except httpx.HTTPError as e:
logger.error(f"HTTP occurred while fetching service analytics data: {e}")
return {}
10 changes: 10 additions & 0 deletions integrations/pagerduty/clients/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from datetime import datetime, timedelta


def get_date_range_for_last_n_months(n: int) -> tuple[str, str]:
now = datetime.utcnow()
start_date = (now - timedelta(days=30 * n)).strftime(
"%Y-%m-%dT%H:%M:%SZ"
) # using ISO 8601 format
end_date = now.strftime("%Y-%m-%dT%H:%M:%SZ")
return (start_date, end_date)
12 changes: 11 additions & 1 deletion integrations/pagerduty/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def generate_request_params(self) -> dict[str, Any]:


class PagerdutyScheduleAPIQueryParams(BaseModel):
include: list[Literal["escalation_policies", "users"]] | None
include: list[str] | None

def generate_request_params(self) -> dict[str, Any]:
value = self.dict(exclude_none=True)
Expand Down Expand Up @@ -103,6 +103,16 @@ class PagerdutySelector(Selector):
api_query_params: PagerdutyServiceAPIQueryParams | None = Field(
alias="apiQueryParams"
)
service_analytics: bool = Field(
default=True,
description="If set to true, will ingest service analytics data to Port. Default value is true",
alias="serviceAnalytics",
)
analytics_months_period: int = Field(
default=3,
description="Number of months to consider for the service analytics date range. Must be a positive integer. Default value is 3 months",
alias="analyticsMonthsPeriod",
)

kind: Literal["services"]
selector: PagerdutySelector
Expand Down
34 changes: 31 additions & 3 deletions integrations/pagerduty/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ def initialize_client() -> PagerDutyClient:
)


async def enrich_service_with_analytics_data(
client: PagerDutyClient, services: list[dict[str, Any]], months_period: int
) -> list[dict[str, Any]]:
analytics_data = await asyncio.gather(
*[
client.get_service_analytics(service["id"], months_period)
for service in services
]
)

enriched_services = [
{**service, "__analytics": analytics}
for service, analytics in zip(services, analytics_data)
]

return enriched_services


async def enrich_incidents_with_analytics_data(
client: PagerDutyClient,
incidents: list[dict[str, Any]],
Expand Down Expand Up @@ -66,14 +84,24 @@ async def on_incidents_resync(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
async def on_services_resync(kind: str) -> ASYNC_GENERATOR_RESYNC_TYPE:
logger.info(f"Listing Pagerduty resource: {kind}")
pager_duty_client = initialize_client()
query_params = typing.cast(

selector = typing.cast(
PagerdutyServiceResourceConfig, event.resource_config
).selector.api_query_params
).selector

async for services in pager_duty_client.paginate_request_to_pager_duty(
data_key=ObjectKind.SERVICES,
params=query_params.generate_request_params() if query_params else None,
params=selector.api_query_params.generate_request_params()
if selector.api_query_params
else None,
):
logger.info(f"Received batch with {len(services)} services")

if selector.service_analytics:
services = await enrich_service_with_analytics_data(
pager_duty_client, services, selector.analytics_months_period
)

yield await pager_duty_client.update_oncall_users(services)


Expand Down
2 changes: 1 addition & 1 deletion integrations/pagerduty/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pagerduty"
version = "0.1.34"
version = "0.1.35"
description = "Pagerduty Integration"
authors = ["Port Team <[email protected]>"]

Expand Down

0 comments on commit e6fb139

Please sign in to comment.