Skip to content
Open
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
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.0.14
current_version = 3.0.0
commit = True
tag = True
tag_name = v{new_version}
Expand Down
2 changes: 2 additions & 0 deletions angles_python_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""

from .http import AnglesHttpClient
from .models.enums import ExecutionTypes
from .reporter import AnglesReporter, angles_reporter
from .requests import (
BuildRequests,
Expand All @@ -22,6 +23,7 @@

__all__ = [
"AnglesHttpClient",
"ExecutionTypes",
"AnglesReporter",
"angles_reporter",
"BuildRequests",
Expand Down
3 changes: 2 additions & 1 deletion angles_python_client/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
DiffRegion,
)

from .enums import ExecutionStates, StepStates, GroupingPeriods
from .enums import ExecutionStates, ExecutionTypes, StepStates, GroupingPeriods

from .requests import (
CreateBuild,
Expand Down Expand Up @@ -56,6 +56,7 @@
"Step",
"Versions",
"ExecutionStates",
"ExecutionTypes",
"StepStates",
"GroupingPeriods",
"CreateBuild",
Expand Down
4 changes: 3 additions & 1 deletion angles_python_client/models/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from .artifact import Artifact
from .environment import Environment
from .enums import ExecutionStates
from .enums import ExecutionStates, ExecutionTypes
from .team import Team


Expand All @@ -24,3 +24,5 @@ class Build:
team: Optional[Team] = None
component: Optional[str] = None
suites: Optional[List[Any]] = None
#: Server-assigned; "automated" unless this build backs a manual test run.
executionType: Optional[ExecutionTypes] = None
13 changes: 13 additions & 0 deletions angles_python_client/models/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ class StepStates(str, Enum):
FAIL = "FAIL"


class ExecutionTypes(str, Enum):
"""Whether a build or execution came from an automated framework or a manual test run.

Read-only from this client's point of view. The value is assigned by the Angles
server - a reporting client setting it to MANUAL would put a build on the dashboard
that no manual test run exists to explain - so it is absent from CreateBuild and
CreateExecution and only ever used to read results back or to filter a list call.
"""

AUTOMATED = "automated"
MANUAL = "manual"


class GroupingPeriods(str, Enum):
DAY = "day"
WEEK = "week"
Expand Down
9 changes: 8 additions & 1 deletion angles_python_client/models/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from .action import Action
from .build import Build
from .enums import ExecutionStates
from .enums import ExecutionStates, ExecutionTypes
from .platform import Platform


Expand All @@ -24,3 +24,10 @@ class Execution:
tags: Optional[List[str]] = None
meta: Optional[Dict[str, Any]] = None
status: Optional[ExecutionStates] = None
#: Server-assigned; "automated" unless this came from a manual test run.
executionType: Optional[ExecutionTypes] = None
#: The fields below are populated only on manual executions.
manualTestCase: Optional[str] = None
manualTestCaseVersion: Optional[str] = None
versionNumber: Optional[int] = None
executedBy: Optional[str] = None
5 changes: 5 additions & 0 deletions angles_python_client/models/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class Period:
result: Optional[Dict[str, Any]] = None
buildCount: Optional[int] = None
phases: Optional[List[Any]] = None
#: {"automated": n, "manual": n} - counts every execution in the period, so a
#: stacked chart built from it matches result["TOTAL"].
executionTypeBreakdown: Optional[Dict[str, int]] = None


@dataclass
Expand All @@ -58,6 +61,8 @@ class PhaseMetrics:
fromDate: Optional[_dt.date] = None
groupingPeriod: Optional[str] = None
periods: Optional[List[Period]] = None
#: Echoes the requested filter; absent when both types are included.
executionType: Optional[str] = None


@dataclass
Expand Down
4 changes: 3 additions & 1 deletion angles_python_client/models/step.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import datetime as _dt
from dataclasses import dataclass
from typing import Optional
from typing import List, Optional

from .enums import StepStates

Expand All @@ -16,3 +16,5 @@ class Step:
status: Optional[StepStates] = None
timestamp: Optional[_dt.datetime] = None
screenshot: Optional[str] = None
#: Attachment ids referenced by a manual step result.
attachments: Optional[List[str]] = None
27 changes: 24 additions & 3 deletions angles_python_client/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@

from ._serialize import jsonable, json_dumps
from .http import AnglesHttpClient
from .models.enums import GroupingPeriods
from .models.enums import ExecutionTypes, GroupingPeriods



def _execution_type_param(execution_type: Optional[ExecutionTypes]) -> Optional[str]:
"""Normalises an ExecutionTypes member (or a plain string) to its wire value."""
if execution_type is None:
return None
return execution_type.value if hasattr(execution_type, "value") else str(execution_type)

class BaseRequests:
def __init__(self, http: AnglesHttpClient):
self.http = http
Expand Down Expand Up @@ -77,21 +84,27 @@ class BuildRequests(BaseRequests):
def create_build(self, request: Any) -> Any:
return self.post("build", request)

def get_builds(self, team_id: str, build_ids: Optional[List[str]] = None, return_execution_details: bool = False) -> Any:
def get_builds(self, team_id: str, build_ids: Optional[List[str]] = None, return_execution_details: bool = False, execution_type: Optional[ExecutionTypes] = None) -> Any:
# matches JS: /build?teamId=...&buildIds=...&returnExecutionDetails=...
params: Dict[str, Any] = {"teamId": team_id}
if build_ids:
params["buildIds"] = ",".join(build_ids)
if return_execution_details:
params["returnExecutionDetails"] = "true"
execution_type_value = _execution_type_param(execution_type)
if execution_type_value:
params["executionType"] = execution_type_value
return self.get("build", params=params)

def get_builds_with_filters(self, team_id: str, filter_environments: Optional[List[str]] = None, filter_components: Optional[List[str]] = None, skip: int = 0, limit: int = 50) -> Any:
def get_builds_with_filters(self, team_id: str, filter_environments: Optional[List[str]] = None, filter_components: Optional[List[str]] = None, skip: int = 0, limit: int = 50, execution_type: Optional[ExecutionTypes] = None) -> Any:
params: Dict[str, Any] = {"teamId": team_id, "skip": skip, "limit": limit}
if filter_environments:
params["environmentIds"] = ",".join(filter_environments)
if filter_components:
params["componentIds"] = ",".join(filter_components)
execution_type_value = _execution_type_param(execution_type)
if execution_type_value:
params["executionType"] = execution_type_value
return self.get("build", params=params)

def get_builds_with_date_filters(
Expand All @@ -103,6 +116,7 @@ def get_builds_with_date_filters(
limit: int = 50,
from_date: Optional[_dt.date] = None,
to_date: Optional[_dt.date] = None,
execution_type: Optional[ExecutionTypes] = None,
) -> Any:
params: Dict[str, Any] = {"teamId": team_id, "skip": skip, "limit": limit}
if from_date:
Expand All @@ -113,6 +127,9 @@ def get_builds_with_date_filters(
params["environmentIds"] = ",".join(filter_environments)
if filter_components:
params["componentIds"] = ",".join(filter_components)
execution_type_value = _execution_type_param(execution_type)
if execution_type_value:
params["executionType"] = execution_type_value
return self.get("build", params=params)

def delete_builds(self, team_id: str, age_in_days: int) -> Any:
Expand Down Expand Up @@ -363,6 +380,7 @@ def get_phase_metrics(
from_date: Optional[_dt.date] = None,
to_date: Optional[_dt.date] = None,
grouping_period: Optional[GroupingPeriods] = None,
execution_type: Optional[ExecutionTypes] = None,
) -> Any:
params: Dict[str, Any] = {"teamId": team_id}
if component_id:
Expand All @@ -373,6 +391,9 @@ def get_phase_metrics(
params["toDate"] = to_date.isoformat()
if grouping_period:
params["groupingPeriod"] = grouping_period.value if hasattr(grouping_period, "value") else str(grouping_period)
execution_type_value = _execution_type_param(execution_type)
if execution_type_value:
params["executionType"] = execution_type_value
# JS uses an absolute URL via new URL(baseURL + '/metrics/phase?...')
# We'll just pass a relative path + params; client will build URL.
return self.get("metrics/phase", params=params)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "angles-python-client"
version = "1.0.14"
version = "3.0.0"
description = "Python client for the Angles Dashboard API"
readme = "README.md"
requires-python = ">=3.9"
Expand Down