"""Monitor API objects for BetterStack Uptime.
This module contains the Monitor and MonitorSLA classes for interacting
with the BetterStack Uptime monitoring API.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, ClassVar
from ..base import BaseAPIObject
if TYPE_CHECKING:
pass
[docs]
@dataclass
class Monitor(BaseAPIObject):
"""Monitor resource from the BetterStack Uptime API.
A monitor represents a URL or service that is being monitored for uptime.
Monitors can check HTTP endpoints, ping hosts, test TCP/UDP ports, and more.
Attributes:
url: The URL of your website or the host you want to ping.
pronounceable_name: Human-readable name used in phone call alerts.
monitor_type: Type of monitor (e.g., 'status', 'keyword', 'ping', 'tcp').
monitor_group_id: ID of the monitor group this monitor belongs to.
last_checked_at: When the monitor was last checked (ISO 8601 datetime).
status: Current status (e.g., 'up', 'down', 'paused', 'maintenance').
policy_id: The escalation policy ID for this monitor.
expiration_policy_id: The expiration escalation policy ID.
team_name: The team this monitor is in.
required_keyword: Keyword that must be present (for keyword/udp monitors).
verify_ssl: Whether to verify SSL certificate validity.
check_frequency: How often to check, in seconds.
call: Whether to call the on-call person on incidents.
sms: Whether to send SMS on incidents.
email: Whether to send email on incidents.
push: Whether to send push notifications on incidents.
critical_alert: Whether to send critical alert push notifications.
team_wait: Seconds to wait before escalating to the team.
http_method: HTTP method for requests (e.g., 'GET', 'POST').
request_timeout: Request timeout in seconds (or ms for server monitors).
recovery_period: Seconds before marking incident as resolved after recovery.
request_headers: Array of custom HTTP headers with 'name' and 'value' properties.
request_body: Request body for POST/PUT/PATCH, or domain for DNS monitors.
paused_at: When the monitor was paused (ISO 8601 datetime), null if not paused.
created_at: When the monitor was created (ISO 8601 datetime).
updated_at: When the monitor was last updated (ISO 8601 datetime).
ssl_expiration: Days before SSL expiration to alert.
domain_expiration: Days before domain expiration to alert.
regions: Array of regions to check from (e.g., 'us', 'eu', 'as', 'au').
port: Port for TCP/UDP/SMTP/POP/IMAP monitors.
confirmation_period: Seconds to wait after failure before starting incident.
expected_status_codes: Array of acceptable HTTP status codes.
maintenance_days: Array of maintenance days (e.g., 'mon', 'tue', 'wed').
maintenance_from: Start of daily maintenance window (e.g., '01:00:00').
maintenance_to: End of daily maintenance window (e.g., '03:00:00').
maintenance_timezone: Timezone for maintenance window.
playwright_script: JavaScript source code for Playwright monitors.
environment_variables: Environment variables for Playwright scenarios.
auth_username: Basic auth username.
auth_password: Basic auth password.
follow_redirects: Whether to follow HTTP redirects.
remember_cookies: Whether to remember cookies between checks.
"""
type: ClassVar[str] = "monitor"
_url_endpoint: ClassVar[str] = "monitors"
_allowed_query_parameters: ClassVar[list[str]] = [
"url",
"pronounceable_name",
"per_page",
"team_name",
]
# Known fields with types
url: str | None = None
pronounceable_name: str | None = None
monitor_type: str | None = None
monitor_group_id: int | None = None
last_checked_at: str | None = None
status: str | None = None
policy_id: int | None = None
expiration_policy_id: int | None = None
team_name: str | None = None
required_keyword: str | None = None
verify_ssl: bool | None = None
check_frequency: int | None = None
call: bool | None = None
sms: bool | None = None
email: bool | None = None
push: bool | None = None
critical_alert: bool | None = None
team_wait: int | None = None
http_method: str | None = None
request_timeout: int | None = None
recovery_period: int | None = None
request_headers: list[dict[str, str]] | None = None
request_body: str | None = None
paused_at: str | None = None
created_at: str | None = None
updated_at: str | None = None
ssl_expiration: int | None = None
domain_expiration: int | None = None
regions: list[str] | None = None
port: str | None = None
confirmation_period: int | None = None
expected_status_codes: list[int] | None = None
maintenance_days: list[str] | None = None
maintenance_from: str | None = None
maintenance_to: str | None = None
maintenance_timezone: str | None = None
playwright_script: str | None = None
environment_variables: dict[str, Any] | None = None
auth_username: str | None = None
auth_password: str | None = None
follow_redirects: bool | None = None
remember_cookies: bool | None = None
# Private fields for lazy-loaded relationships
_sla: MonitorSLA | None = field(default=None, repr=False, compare=False)
@property
def sla(self) -> MonitorSLA:
"""Get or create the SLA object for this monitor.
Returns:
MonitorSLA object for this monitor.
"""
if self._sla is None:
self._sla = MonitorSLA(id=self.id, _api=self._api)
return self._sla
@property
def is_paused(self) -> bool:
"""Check if the monitor is currently paused.
Returns:
True if the monitor is paused, False otherwise.
"""
return self.paused_at is not None
[docs]
@dataclass
class MonitorSLA(BaseAPIObject):
"""Monitor SLA (Service Level Agreement) data.
This object provides SLA statistics for a specific monitor
over a given time period.
Attributes:
availability: Availability percentage (0-100).
downtime_duration: Total downtime in seconds.
number_of_incidents: Number of incidents in the period.
longest_incident: Duration of longest incident in seconds.
average_incident: Average incident duration in seconds.
"""
type: ClassVar[str] = "monitor_sla"
_url_endpoint: ClassVar[str] = "monitors/{id}/sla"
_allowed_query_parameters: ClassVar[list[str]] = ["from", "to"]
# Known fields with types
availability: float | None = None
downtime_duration: int | None = None
number_of_incidents: int | None = None
longest_incident: int | None = None
average_incident: int | None = None
# Private fields for timeframe
_sla_start: str | None = field(default=None, repr=False, compare=False)
_sla_end: str | None = field(default=None, repr=False, compare=False)
[docs]
def generate_url(self) -> str:
"""Create the URL for this SLA endpoint.
Returns:
Full SLA URL path.
"""
return f"monitors/{self.id}/sla"
[docs]
@classmethod
def generate_global_url(cls) -> str:
"""SLA objects don't have a global URL.
Raises:
ValueError: Always, as SLA has no collection endpoint.
"""
raise ValueError("No overview available for SLA objects")
@property
def timeframe(self) -> tuple[str | None, str | None]:
"""Get the current SLA timeframe.
Returns:
Tuple of (start_date, end_date).
"""
return (self._sla_start, self._sla_end)
@timeframe.setter
def timeframe(self, frame: tuple[str, str]) -> None:
"""Set the SLA timeframe and fetch new data.
Args:
frame: Tuple of (start_date, end_date) in ISO 8601 format.
"""
start, end = frame
self._sla_start = start
self._sla_end = end
self.fetch_data(from_=start, to=end)
[docs]
def fetch_sla(self, from_date: str, to_date: str) -> None:
"""Fetch SLA data for a specific time period.
Args:
from_date: Start date in ISO 8601 format.
to_date: End date in ISO 8601 format.
"""
self.timeframe = (from_date, to_date)