Source code for betterstack.uptime.objects.on_call

"""OnCallCalendar API objects for BetterStack Uptime."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, ClassVar

from ..base import BaseAPIObject

if TYPE_CHECKING:
    from ..api import PaginatedAPI


[docs] @dataclass class OnCallCalendar(BaseAPIObject): """OnCallCalendar resource from the BetterStack Uptime API. An on-call calendar (schedule) defines who is on-call and when. It manages on-call rotations and events for incident response. Attributes: name: The name of the schedule. default_calendar: Whether this is the default calendar for the team. team_name: Name of the team associated with the schedule. """ type: ClassVar[str] = "on_call_calendar" _url_endpoint: ClassVar[str] = "on-calls" _allowed_query_parameters: ClassVar[list[str]] = ["per_page", "team_name"] # Known fields with types name: str | None = None default_calendar: bool | None = None team_name: str | None = None # Private fields for lazy-loaded relationships _on_call_users: list[dict[str, Any]] | None = field(default=None, repr=False, compare=False) _events: list[OnCallEvent] | None = field(default=None, repr=False, compare=False) @classmethod def _from_api_response( cls, api: PaginatedAPI, data: dict[str, Any], ) -> OnCallCalendar: """Create an OnCallCalendar from API response data. This override handles the relationships.on_call_users data. Args: api: API instance for further requests. data: Raw API response data. Returns: OnCallCalendar instance. """ instance = super()._from_api_response(api, data) # Extract on_call_users from relationships relationships = data.get("relationships", {}) on_call_users_data = relationships.get("on_call_users", {}).get("data", []) instance._on_call_users = on_call_users_data return instance @property def on_call_users(self) -> list[dict[str, Any]]: """Get the current on-call users for this calendar. Returns: List of user data dictionaries. """ if self._on_call_users is None: return [] return self._on_call_users @property def events(self) -> list[OnCallEvent]: """Get the events for this on-call calendar (lazy-loaded). Returns: List of OnCallEvent objects. """ if self._events is None: self._events = self.fetch_events() return self._events
[docs] def fetch_events(self) -> list[OnCallEvent]: """Fetch all events for this on-call calendar from the API. Returns: List of OnCallEvent objects. """ if self._api is None: raise ValueError("API not set") url = f"on-calls/{self.id}/events" events = [] for item in self._api.get(url): event = OnCallEvent._from_api_response(self._api, item) event._calendar_id = self.id events.append(event) self._events = events return events
[docs] def create_rotation( self, users: list[str], rotation_length: int, rotation_period: str, start_rotations_at: str, end_rotations_at: str, ) -> dict[str, Any]: """Create an on-call rotation for this calendar. Args: users: List of email addresses of users to include. rotation_length: Length of the rotation period. rotation_period: Period type ('hour', 'day', 'week'). start_rotations_at: Start time in ISO 8601 format. end_rotations_at: End time in ISO 8601 format. Returns: API response data for the created rotation. """ if self._api is None: raise ValueError("API not set") url = f"on-calls/{self.id}/rotation" data = { "users": users, "rotation_length": rotation_length, "rotation_period": rotation_period, "start_rotations_at": start_rotations_at, "end_rotations_at": end_rotations_at, } response = self._api.post(url, body=data) return response.json()
[docs] @dataclass class OnCallEvent(BaseAPIObject): """OnCallEvent resource from the BetterStack Uptime API. An on-call event represents a time period when specific users are on-call. Attributes: starts_at: Start time of the event in ISO 8601 format. ends_at: End time of the event in ISO 8601 format. users: List of email addresses of users on-call during this event. override: Whether this is an override event. """ type: ClassVar[str] = "on_call_event" _url_endpoint: ClassVar[str] = "on-calls/{calendar_id}/events" _allowed_query_parameters: ClassVar[list[str]] = ["per_page"] # Known fields with types starts_at: str | None = None ends_at: str | None = None users: list[str] | None = None override: bool | None = None # Private fields _calendar_id: str | None = field(default=None, repr=False, compare=False)
[docs] def generate_url(self) -> str: """Create the URL for this event endpoint. Returns: Full event URL path. Raises: ValueError: If calendar_id is not set. """ if self._calendar_id is None: raise ValueError("calendar_id is required to generate URL") return f"on-calls/{self._calendar_id}/events/{self.id}"
[docs] @classmethod def generate_global_url(cls) -> str: """Events don't have a global URL without a calendar ID. Raises: ValueError: Always, as events require a calendar ID. """ raise ValueError("OnCallEvent requires a calendar_id to generate URL")