plotly-cloud 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/PKG-INFO +1 -1
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/__init__.py +1 -1
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_api_types.py +27 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_commands.py +178 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_deploy.py +167 -6
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/pyproject.toml +1 -1
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/.gitignore +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/LICENSE +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/README.md +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_changes.py +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_cloud_env.py +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_definitions.py +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_devtool_hooks.py +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_devtool_publish_rpc.py +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_oauth.py +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_parser.py +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/_run_sync.py +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/cli.py +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/cloud-env.toml +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/cloud_devtools.css +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/cloud_devtools.js +0 -0
- {plotly_cloud-0.3.0 → plotly_cloud-0.4.0}/plotly_cloud/exceptions.py +0 -0
|
@@ -56,3 +56,30 @@ class App(TypedDict):
|
|
|
56
56
|
class UserTeamMembership(TypedDict):
|
|
57
57
|
team: Team
|
|
58
58
|
role: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Build(TypedDict):
|
|
62
|
+
id: NotRequired[str]
|
|
63
|
+
revisionId: NotRequired[str]
|
|
64
|
+
imageRef: NotRequired[str]
|
|
65
|
+
sourceKey: NotRequired[str]
|
|
66
|
+
pythonVersion: NotRequired[str]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class Revision(TypedDict):
|
|
70
|
+
id: str
|
|
71
|
+
workloadId: NotRequired[str]
|
|
72
|
+
revisionNumber: NotRequired[int]
|
|
73
|
+
status: NotRequired[str]
|
|
74
|
+
replicaSet: NotRequired[str]
|
|
75
|
+
createdBy: NotRequired[str]
|
|
76
|
+
createdByEmail: NotRequired[str]
|
|
77
|
+
createdByFirstName: NotRequired[str]
|
|
78
|
+
createdByLastName: NotRequired[str]
|
|
79
|
+
build: NotRequired[Build]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class DashlogsNotFound(TypedDict):
|
|
83
|
+
error: str
|
|
84
|
+
reason: str
|
|
85
|
+
retention_days: int
|
|
@@ -34,6 +34,7 @@ from plotly_cloud._parser import ParsedArguments
|
|
|
34
34
|
|
|
35
35
|
from .exceptions import (
|
|
36
36
|
ApplicationError,
|
|
37
|
+
AuthenticationError,
|
|
37
38
|
CredentialError,
|
|
38
39
|
DashAppError,
|
|
39
40
|
ModuleImportError,
|
|
@@ -787,6 +788,95 @@ class PublishCommand(BaseCommand):
|
|
|
787
788
|
console.print(f"Your app is available at: {format_app_url(app_url)}")
|
|
788
789
|
|
|
789
790
|
|
|
791
|
+
class ListCommand(BaseCommand):
|
|
792
|
+
"""List all apps for the authenticated user."""
|
|
793
|
+
|
|
794
|
+
name = "list"
|
|
795
|
+
group = "app"
|
|
796
|
+
short_description = "📋 List all your published apps"
|
|
797
|
+
description = "Display all applications published to Plotly Cloud."
|
|
798
|
+
arguments: List[CommandArgument] = [
|
|
799
|
+
{
|
|
800
|
+
"name": "--team",
|
|
801
|
+
"help": "Filter apps by team name or slug",
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
"name": "--team-id",
|
|
805
|
+
"help": "Filter apps by team ID",
|
|
806
|
+
},
|
|
807
|
+
]
|
|
808
|
+
|
|
809
|
+
@classmethod
|
|
810
|
+
async def execute(cls, args: ParsedArguments) -> None:
|
|
811
|
+
"""Execute list command."""
|
|
812
|
+
client_id = cloud_config.get_oauth_client_id()
|
|
813
|
+
oauth_client = OAuthClient(client_id, token=getattr(args, "api_key", None))
|
|
814
|
+
|
|
815
|
+
if not await oauth_client.is_authenticated():
|
|
816
|
+
raise ApplicationError("Not authenticated. Please run 'plotly login' first.")
|
|
817
|
+
|
|
818
|
+
auth_token = await oauth_client.get_access_token()
|
|
819
|
+
if not auth_token:
|
|
820
|
+
raise ApplicationError("Unable to retrieve access token. Please try logging in again.")
|
|
821
|
+
|
|
822
|
+
async with DeploymentClient(oauth_client) as deploy_client:
|
|
823
|
+
# Resolve team name/slug to team_id if needed
|
|
824
|
+
team_id = getattr(args, "team_id", None)
|
|
825
|
+
team_name_or_slug = getattr(args, "team", None)
|
|
826
|
+
|
|
827
|
+
if team_name_or_slug and not team_id:
|
|
828
|
+
teams = await deploy_client.list_teams()
|
|
829
|
+
for team in teams:
|
|
830
|
+
if (
|
|
831
|
+
team.get("name", "").lower() == team_name_or_slug.lower()
|
|
832
|
+
or team.get("team_slug", "").lower() == team_name_or_slug.lower()
|
|
833
|
+
or team.get("id", "").lower() == team_name_or_slug.lower()
|
|
834
|
+
):
|
|
835
|
+
team_id = team.get("id")
|
|
836
|
+
break
|
|
837
|
+
if not team_id:
|
|
838
|
+
raise ApplicationError(
|
|
839
|
+
f"Team '{team_name_or_slug}' not found. Use 'plotly user teams' to list available teams."
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
apps = await deploy_client.list_apps(team_id)
|
|
843
|
+
|
|
844
|
+
if not apps:
|
|
845
|
+
console.print(Panel("No apps found.", title="📋 Apps", border_style="yellow"))
|
|
846
|
+
return
|
|
847
|
+
|
|
848
|
+
table = Table(show_header=True, header_style="bold blue", expand=True)
|
|
849
|
+
table.add_column("Name", style="cyan", no_wrap=True, ratio=3)
|
|
850
|
+
table.add_column("Status", style="white", no_wrap=True, ratio=1)
|
|
851
|
+
table.add_column("App ID", style="dim", no_wrap=True, ratio=1)
|
|
852
|
+
table.add_column("Published", style="magenta", no_wrap=True, ratio=1)
|
|
853
|
+
|
|
854
|
+
for app in apps:
|
|
855
|
+
status = app.get("status", "—")
|
|
856
|
+
status_info = REVISION_STATUS_MAP.get(status, {"label": status, "emoji": "?", "color": "white"})
|
|
857
|
+
status_display = (
|
|
858
|
+
f"{status_info['emoji']} [{status_info['color']}]{status_info['label']}[/{status_info['color']}]"
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
last_published = app.get("last_published", "—")
|
|
862
|
+
if last_published and last_published != "—":
|
|
863
|
+
last_published = last_published[:10]
|
|
864
|
+
|
|
865
|
+
app_id = app.get("id", "—")
|
|
866
|
+
app_id_short = app_id[:8] if app_id != "—" else "—"
|
|
867
|
+
|
|
868
|
+
table.add_row(
|
|
869
|
+
app.get("name", "—"),
|
|
870
|
+
status_display,
|
|
871
|
+
app_id_short,
|
|
872
|
+
last_published,
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
console.print()
|
|
876
|
+
console.print(Panel.fit(table, title="📋 Your Apps", border_style="bold blue"))
|
|
877
|
+
console.print()
|
|
878
|
+
|
|
879
|
+
|
|
790
880
|
class StatusCommand(BaseCommand):
|
|
791
881
|
"""Get the status of an app published to Plotly Cloud."""
|
|
792
882
|
|
|
@@ -932,6 +1022,94 @@ class TeamsCommand(BaseCommand):
|
|
|
932
1022
|
console.print()
|
|
933
1023
|
|
|
934
1024
|
|
|
1025
|
+
class LogsCommand(BaseCommand):
|
|
1026
|
+
"""Get build or runtime logs for a published app."""
|
|
1027
|
+
|
|
1028
|
+
name = "logs"
|
|
1029
|
+
group = "app"
|
|
1030
|
+
short_description = "📋 Get logs for a published app"
|
|
1031
|
+
description = "Retrieve build or runtime logs for your published application."
|
|
1032
|
+
arguments: List[CommandArgument] = [
|
|
1033
|
+
{
|
|
1034
|
+
"name": "--project-path",
|
|
1035
|
+
"default": ".",
|
|
1036
|
+
"help": "Path to project directory",
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
"name": "--config",
|
|
1040
|
+
"default": "plotly-cloud.toml",
|
|
1041
|
+
"help": "Path to configuration file",
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
"name": "--type",
|
|
1045
|
+
"default": "build",
|
|
1046
|
+
"choices": ["build", "runtime"],
|
|
1047
|
+
"help": "Type of logs to retrieve (default: build)",
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
"name": "--revision",
|
|
1051
|
+
"help": "Revision ID (default: latest revision)",
|
|
1052
|
+
},
|
|
1053
|
+
]
|
|
1054
|
+
|
|
1055
|
+
@classmethod
|
|
1056
|
+
async def execute(cls, args: ParsedArguments) -> None:
|
|
1057
|
+
"""Execute logs command."""
|
|
1058
|
+
project_path = os.path.abspath(args.project_path)
|
|
1059
|
+
config_path = get_config_path(project_path, args.config)
|
|
1060
|
+
config = load_deployment_config(config_path)
|
|
1061
|
+
|
|
1062
|
+
app_id = config.get("app_id")
|
|
1063
|
+
if not app_id:
|
|
1064
|
+
raise ApplicationError("No app_id found in configuration. Publish your app first using 'plotly publish'.")
|
|
1065
|
+
|
|
1066
|
+
client_id = cloud_config.get_oauth_client_id()
|
|
1067
|
+
oauth_client = OAuthClient(client_id, token=getattr(args, "api_key", None))
|
|
1068
|
+
|
|
1069
|
+
if not await oauth_client.is_authenticated():
|
|
1070
|
+
raise AuthenticationError("Not authenticated. Please run 'plotly login' first.")
|
|
1071
|
+
|
|
1072
|
+
auth_token = await oauth_client.get_access_token()
|
|
1073
|
+
if not auth_token:
|
|
1074
|
+
raise AuthenticationError("Unable to retrieve access token. Please try logging in again.")
|
|
1075
|
+
|
|
1076
|
+
log_type = getattr(args, "type", "build")
|
|
1077
|
+
revision_id = getattr(args, "revision", None)
|
|
1078
|
+
|
|
1079
|
+
async with DeploymentClient(oauth_client) as deploy_client:
|
|
1080
|
+
with Progress(
|
|
1081
|
+
SpinnerColumn(),
|
|
1082
|
+
TextColumn("[progress.description]{task.description}"),
|
|
1083
|
+
console=console,
|
|
1084
|
+
) as progress:
|
|
1085
|
+
# Resolve revision ID if not provided
|
|
1086
|
+
if not revision_id:
|
|
1087
|
+
task = progress.add_task("Fetching latest revision...", total=None)
|
|
1088
|
+
revisions = await deploy_client.get_app_revisions(app_id)
|
|
1089
|
+
if not revisions:
|
|
1090
|
+
raise ApplicationError("No revisions found for this app.")
|
|
1091
|
+
revision_id = revisions[0]["id"]
|
|
1092
|
+
progress.update(task, description=f"✓ Using revision {revision_id[:8]}...")
|
|
1093
|
+
|
|
1094
|
+
# Fetch logs
|
|
1095
|
+
task = progress.add_task(f"Fetching {log_type} logs...", total=None)
|
|
1096
|
+
log_text, _ = await deploy_client.get_logs(app_id, revision_id, log_type)
|
|
1097
|
+
progress.remove_task(task)
|
|
1098
|
+
|
|
1099
|
+
if not log_text.strip():
|
|
1100
|
+
console.print(f"No {log_type} logs available.")
|
|
1101
|
+
return
|
|
1102
|
+
|
|
1103
|
+
console.print(
|
|
1104
|
+
Panel(
|
|
1105
|
+
log_text,
|
|
1106
|
+
title=f"📋 {log_type.title()} Logs",
|
|
1107
|
+
border_style="blue",
|
|
1108
|
+
expand=True,
|
|
1109
|
+
)
|
|
1110
|
+
)
|
|
1111
|
+
|
|
1112
|
+
|
|
935
1113
|
class WhoamiCommand(BaseCommand):
|
|
936
1114
|
"""Show current user information."""
|
|
937
1115
|
|
|
@@ -11,7 +11,7 @@ import httpx
|
|
|
11
11
|
import tomli
|
|
12
12
|
import tomli_w
|
|
13
13
|
|
|
14
|
-
from plotly_cloud._api_types import App, AppRequest, Team
|
|
14
|
+
from plotly_cloud._api_types import App, AppRequest, DashlogsNotFound, Revision, Team, UserTeamMembership
|
|
15
15
|
from plotly_cloud._cloud_env import cloud_config
|
|
16
16
|
from plotly_cloud._definitions import AppDeploymentConfig
|
|
17
17
|
from plotly_cloud._oauth import OAuthClient
|
|
@@ -112,9 +112,7 @@ def should_exclude_path(path: str, exclude_patterns: set[str]) -> bool:
|
|
|
112
112
|
return False
|
|
113
113
|
|
|
114
114
|
|
|
115
|
-
async def create_deployment_zip(
|
|
116
|
-
project_path: str, output_path: str, skip_size_check: bool = False
|
|
117
|
-
) -> int:
|
|
115
|
+
async def create_deployment_zip(project_path: str, output_path: str, skip_size_check: bool = False) -> int:
|
|
118
116
|
"""
|
|
119
117
|
Create a zip file for deployment, excluding files based on .gitignore.
|
|
120
118
|
|
|
@@ -512,8 +510,8 @@ class DeploymentClient:
|
|
|
512
510
|
response = await self._client.get(url)
|
|
513
511
|
|
|
514
512
|
if response.status_code == 200:
|
|
515
|
-
|
|
516
|
-
return
|
|
513
|
+
memberships: list[UserTeamMembership] = response.json()
|
|
514
|
+
return [m["team"] for m in memberships]
|
|
517
515
|
|
|
518
516
|
token_refreshed = await self._refresh_token_if_needed(response)
|
|
519
517
|
if token_refreshed:
|
|
@@ -524,3 +522,166 @@ class DeploymentClient:
|
|
|
524
522
|
raise _handle_error_response(response, "list teams")
|
|
525
523
|
except httpx.RequestError as e:
|
|
526
524
|
raise NetworkError("Failed to list teams", str(e)) from e
|
|
525
|
+
|
|
526
|
+
async def list_apps(self, team_id: Optional[str] = None) -> list[App]:
|
|
527
|
+
"""List all apps for the authenticated user.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
team_id: Optional team ID to filter apps by
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
List of App objects
|
|
534
|
+
|
|
535
|
+
Raises:
|
|
536
|
+
DeploymentClientError: If client is not initialized
|
|
537
|
+
APIError: If API call fails
|
|
538
|
+
NetworkError: If network request fails
|
|
539
|
+
"""
|
|
540
|
+
if not self._client:
|
|
541
|
+
raise DeploymentClientError("Client not initialized. Use within async context manager.")
|
|
542
|
+
|
|
543
|
+
url = f"https://{self.api_base_url}/api/apps"
|
|
544
|
+
params = {}
|
|
545
|
+
if team_id:
|
|
546
|
+
params["teamId"] = team_id
|
|
547
|
+
|
|
548
|
+
try:
|
|
549
|
+
retry_count = 0
|
|
550
|
+
response = None
|
|
551
|
+
while retry_count <= 1:
|
|
552
|
+
response = await self._client.get(url, params=params)
|
|
553
|
+
|
|
554
|
+
if response.status_code == 200:
|
|
555
|
+
apps: list[App] = response.json()
|
|
556
|
+
return apps
|
|
557
|
+
|
|
558
|
+
token_refreshed = await self._refresh_token_if_needed(response)
|
|
559
|
+
if token_refreshed:
|
|
560
|
+
retry_count += 1
|
|
561
|
+
else:
|
|
562
|
+
break
|
|
563
|
+
|
|
564
|
+
raise _handle_error_response(response, "list apps")
|
|
565
|
+
except httpx.RequestError as e:
|
|
566
|
+
raise NetworkError("Failed to list apps", str(e)) from e
|
|
567
|
+
|
|
568
|
+
async def get_app_revisions(self, app_id: str) -> list[Revision]:
|
|
569
|
+
"""Get revisions for an application.
|
|
570
|
+
|
|
571
|
+
Args:
|
|
572
|
+
app_id: Application ID
|
|
573
|
+
|
|
574
|
+
Returns:
|
|
575
|
+
List of Revision objects
|
|
576
|
+
|
|
577
|
+
Raises:
|
|
578
|
+
DeploymentClientError: If client is not initialized
|
|
579
|
+
APIError: If API call fails
|
|
580
|
+
NetworkError: If network request fails
|
|
581
|
+
"""
|
|
582
|
+
if not self._client:
|
|
583
|
+
raise DeploymentClientError("Client not initialized. Use within async context manager.")
|
|
584
|
+
|
|
585
|
+
url = f"https://{self.api_base_url}/api/app/{app_id}/revisions"
|
|
586
|
+
|
|
587
|
+
try:
|
|
588
|
+
retry_count = 0
|
|
589
|
+
response = None
|
|
590
|
+
while retry_count <= 1:
|
|
591
|
+
response = await self._client.get(url)
|
|
592
|
+
|
|
593
|
+
if response.status_code == 200:
|
|
594
|
+
data = response.json()
|
|
595
|
+
revisions: list[Revision] = data.get("revisions", data) if isinstance(data, dict) else data
|
|
596
|
+
return revisions
|
|
597
|
+
|
|
598
|
+
token_refreshed = await self._refresh_token_if_needed(response)
|
|
599
|
+
if token_refreshed:
|
|
600
|
+
retry_count += 1
|
|
601
|
+
else:
|
|
602
|
+
break
|
|
603
|
+
|
|
604
|
+
raise _handle_error_response(response, "get app revisions")
|
|
605
|
+
except httpx.RequestError as e:
|
|
606
|
+
raise NetworkError("Failed to get app revisions", str(e)) from e
|
|
607
|
+
|
|
608
|
+
async def get_logs(
|
|
609
|
+
self,
|
|
610
|
+
app_id: str,
|
|
611
|
+
revision_id: str,
|
|
612
|
+
log_type: str = "build",
|
|
613
|
+
continue_token: Optional[str] = None,
|
|
614
|
+
) -> tuple[str, Optional[str]]:
|
|
615
|
+
"""Get build or runtime logs for an app revision.
|
|
616
|
+
|
|
617
|
+
Uses the dashlogs streaming endpoints which return log lines
|
|
618
|
+
followed by a JSON meta line with continuation token.
|
|
619
|
+
|
|
620
|
+
Args:
|
|
621
|
+
app_id: Application ID
|
|
622
|
+
revision_id: Revision ID
|
|
623
|
+
log_type: "build" or "runtime"
|
|
624
|
+
continue_token: Continuation token from a previous response
|
|
625
|
+
|
|
626
|
+
Returns:
|
|
627
|
+
Tuple of (log_text, next_continue_token).
|
|
628
|
+
next_continue_token is None if there are no more logs.
|
|
629
|
+
|
|
630
|
+
Raises:
|
|
631
|
+
DeploymentClientError: If client is not initialized
|
|
632
|
+
APIError: If API call fails (including 202 when logs not available)
|
|
633
|
+
NetworkError: If network request fails
|
|
634
|
+
"""
|
|
635
|
+
if not self._client:
|
|
636
|
+
raise DeploymentClientError("Client not initialized. Use within async context manager.")
|
|
637
|
+
|
|
638
|
+
url = f"https://{self.api_base_url}/api/app/{app_id}/dashlogs/{revision_id}/{log_type}"
|
|
639
|
+
params = {}
|
|
640
|
+
if continue_token:
|
|
641
|
+
params["continue"] = continue_token
|
|
642
|
+
|
|
643
|
+
try:
|
|
644
|
+
retry_count = 0
|
|
645
|
+
response = None
|
|
646
|
+
while retry_count <= 1:
|
|
647
|
+
response = await self._client.get(url, params=params)
|
|
648
|
+
|
|
649
|
+
if response.status_code == 200:
|
|
650
|
+
body = response.text
|
|
651
|
+
# The last line is a JSON meta line with continuation info
|
|
652
|
+
lines = body.rstrip("\n").split("\n")
|
|
653
|
+
next_token = None
|
|
654
|
+
log_lines = lines
|
|
655
|
+
if lines:
|
|
656
|
+
try:
|
|
657
|
+
meta = json.loads(lines[-1])
|
|
658
|
+
next_token = meta.get("next_token")
|
|
659
|
+
log_lines = lines[:-1]
|
|
660
|
+
except (json.JSONDecodeError, ValueError):
|
|
661
|
+
pass
|
|
662
|
+
return "\n".join(log_lines), next_token
|
|
663
|
+
|
|
664
|
+
if response.status_code == 202:
|
|
665
|
+
not_found: DashlogsNotFound = response.json()
|
|
666
|
+
reason = not_found.get("reason", "unknown")
|
|
667
|
+
if reason == "expired":
|
|
668
|
+
raise APIError(
|
|
669
|
+
"Logs have expired",
|
|
670
|
+
status_code=202,
|
|
671
|
+
details=f"Logs are deleted after {not_found.get('retention_days', '?')} days",
|
|
672
|
+
)
|
|
673
|
+
raise APIError(
|
|
674
|
+
"Logs not available yet",
|
|
675
|
+
status_code=202,
|
|
676
|
+
details="Build may still be in progress",
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
token_refreshed = await self._refresh_token_if_needed(response)
|
|
680
|
+
if token_refreshed:
|
|
681
|
+
retry_count += 1
|
|
682
|
+
else:
|
|
683
|
+
break
|
|
684
|
+
|
|
685
|
+
raise _handle_error_response(response, f"get {log_type} logs")
|
|
686
|
+
except httpx.RequestError as e:
|
|
687
|
+
raise NetworkError(f"Failed to get {log_type} logs", str(e)) from e
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|