Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Notify Documentation Update

on:
push:
branches: [main]
paths:
- "docs/**"
- "scripts/make_docs.py"
workflow_dispatch:

jobs:
notify-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
id: app-token
with:
app-id: ${{ vars.UPDATE_DOCS_APP_ID }}
private-key: ${{ secrets.UPDATE_DOCS_PRIVATE_KEY }}
owner: "${{ github.repository_owner }}"
repositories: |
sdk
prod-docs

- name: Trigger docs repository workflow
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0
with:
token: ${{ steps.app-token.outputs.token }}
repository: dreadnode/prod-docs
event-type: code-update
client-payload: '{"repository": "${{ github.repository }}", "ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "product": "strikes", "docs_dir": "docs", "module_dir": "dreadnode"}'
128 changes: 122 additions & 6 deletions dreadnode/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@


class ApiClient:
"""Client for the Dreadnode API."""
"""Client for the Dreadnode API.

This class provides methods to interact with the Dreadnode API, including
retrieving projects, runs, tasks, and exporting data.
"""

def __init__(
self,
Expand All @@ -46,6 +50,13 @@ def __init__(
*,
debug: bool = False,
):
"""Initializes the API client.

Args:
base_url (str): The base URL of the Dreadnode API.
api_key (str): The API key for authentication.
debug (bool, optional): Whether to enable debug logging. Defaults to False.
"""
self._base_url = base_url.rstrip("/")
if not self._base_url.endswith("/api"):
self._base_url += "/api"
Expand All @@ -65,7 +76,11 @@ def __init__(
self._client.event_hooks["response"].append(self._log_response)

def _log_request(self, request: httpx.Request) -> None:
"""Log every request to the console if debug is enabled."""
"""Logs HTTP requests if debug mode is enabled.

Args:
request (httpx.Request): The HTTP request object.
"""

logger.debug("-------------------------------------------")
logger.debug("%s %s", request.method, request.url)
Expand All @@ -74,7 +89,11 @@ def _log_request(self, request: httpx.Request) -> None:
logger.debug("-------------------------------------------")

def _log_response(self, response: httpx.Response) -> None:
"""Log every response to the console if debug is enabled."""
"""Logs HTTP responses if debug mode is enabled.

Args:
response (httpx.Response): The HTTP response object.
"""

logger.debug("-------------------------------------------")
logger.debug("Response: %s", response.status_code)
Expand All @@ -83,7 +102,14 @@ def _log_response(self, response: httpx.Response) -> None:
logger.debug("--------------------------------------------")

def _get_error_message(self, response: httpx.Response) -> str:
"""Get the error message from the response."""
"""Extracts the error message from an HTTP response.

Args:
response (httpx.Response): The HTTP response object.

Returns:
str: The error message extracted from the response.
"""

try:
obj = response.json()
Expand All @@ -98,7 +124,17 @@ def _request(
params: dict[str, t.Any] | None = None,
json_data: dict[str, t.Any] | None = None,
) -> httpx.Response:
"""Make a raw request to the API."""
"""Makes a raw HTTP request to the API.

Args:
method (str): The HTTP method (e.g., "GET", "POST").
path (str): The API endpoint path.
params (dict[str, Any] | None, optional): Query parameters for the request. Defaults to None.
json_data (dict[str, Any] | None, optional): JSON payload for the request. Defaults to None.

Returns:
httpx.Response: The HTTP response object.
"""

return self._client.request(method, path, json=json_data, params=params)

Expand All @@ -109,7 +145,20 @@ def request(
params: dict[str, t.Any] | None = None,
json_data: dict[str, t.Any] | None = None,
) -> httpx.Response:
"""Make a request to the API. Raise an exception for non-200 status codes."""
"""Makes an HTTP request to the API and raises exceptions for errors.

Args:
method (str): The HTTP method (e.g., "GET", "POST").
path (str): The API endpoint path.
params (dict[str, Any] | None, optional): Query parameters for the request. Defaults to None.
json_data (dict[str, Any] | None, optional): JSON payload for the request. Defaults to None.

Returns:
httpx.Response: The HTTP response object.

Raises:
RuntimeError: If the response status code indicates an error.
"""

response = self._request(method, path, params, json_data)
if response.status_code == 401: # noqa: PLR2004
Expand All @@ -123,10 +172,23 @@ def request(
return response

def list_projects(self) -> list[Project]:
"""Retrieves a list of projects.

Returns:
list[Project]: A list of Project objects.
"""
response = self.request("GET", "/strikes/projects")
return [Project(**project) for project in response.json()]

def get_project(self, project: str) -> Project:
"""Retrieves details of a specific project.

Args:
project (str): The project identifier.

Returns:
Project: The Project object.
"""
response = self.request("GET", f"/strikes/projects/{project!s}")
return Project(**response.json())

Expand Down Expand Up @@ -195,6 +257,17 @@ def export_runs(
status: StatusFilter = "completed",
aggregations: list[MetricAggregationType] | None = None,
) -> pd.DataFrame:
"""Exports run data for a specific project.

Args:
project (str): The project identifier.
filter (str | None, optional): A filter to apply to the exported data. Defaults to None.
status (StatusFilter, optional): The status of runs to include. Defaults to "completed".
aggregations (list[MetricAggregationType] | None, optional): A list of aggregation types to apply. Defaults to None.

Returns:
pd.DataFrame: A DataFrame containing the exported run data.
"""
response = self.request(
"GET",
f"/strikes/projects/{project!s}/export",
Expand All @@ -217,6 +290,18 @@ def export_metrics(
metrics: list[str] | None = None,
aggregations: list[MetricAggregationType] | None = None,
) -> pd.DataFrame:
"""Exports metric data for a specific project.

Args:
project (str): The project identifier.
filter (str | None, optional): A filter to apply to the exported data. Defaults to None.
status (StatusFilter, optional): The status of metrics to include. Defaults to "completed".
metrics (list[str] | None, optional): A list of metric names to include. Defaults to None.
aggregations (list[MetricAggregationType] | None, optional): A list of aggregation types to apply. Defaults to None.

Returns:
pd.DataFrame: A DataFrame containing the exported metric data.
"""
response = self.request(
"GET",
f"/strikes/projects/{project!s}/export/metrics",
Expand All @@ -241,6 +326,19 @@ def export_parameters(
metrics: list[str] | None = None,
aggregations: list[MetricAggregationType] | None = None,
) -> pd.DataFrame:
"""Exports parameter data for a specific project.

Args:
project (str): The project identifier.
filter (str | None, optional): A filter to apply to the exported data. Defaults to None.
status (StatusFilter, optional): The status of parameters to include. Defaults to "completed".
parameters (list[str] | None, optional): A list of parameter names to include. Defaults to None.
metrics (list[str] | None, optional): A list of metric names to include. Defaults to None.
aggregations (list[MetricAggregationType] | None, optional): A list of aggregation types to apply. Defaults to None.

Returns:
pd.DataFrame: A DataFrame containing the exported parameter data.
"""
response = self.request(
"GET",
f"/strikes/projects/{project!s}/export/parameters",
Expand All @@ -266,6 +364,19 @@ def export_timeseries(
time_axis: TimeAxisType = "relative",
aggregations: list[TimeAggregationType] | None = None,
) -> pd.DataFrame:
"""Exports timeseries data for a specific project.

Args:
project (str): The project identifier.
filter (str | None, optional): A filter to apply to the exported data. Defaults to None.
status (StatusFilter, optional): The status of timeseries to include. Defaults to "completed".
metrics (list[str] | None, optional): A list of metric names to include. Defaults to None.
time_axis (TimeAxisType, optional): The type of time axis to use. Defaults to "relative".
aggregations (list[TimeAggregationType] | None, optional): A list of aggregation types to apply. Defaults to None.

Returns:
pd.DataFrame: A DataFrame containing the exported timeseries data.
"""
response = self.request(
"GET",
f"/strikes/projects/{project!s}/export/timeseries",
Expand All @@ -283,5 +394,10 @@ def export_timeseries(
# User data access

def get_user_data_credentials(self) -> UserDataCredentials:
"""Retrieves user data credentials.

Returns:
UserDataCredentials: The user data credentials object.
"""
response = self.request("GET", "/user-data/credentials")
return UserDataCredentials(**response.json())
Loading