gmicloud 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl

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.
gmicloud/__init__.py CHANGED
@@ -1,3 +1,6 @@
1
+ import logging
2
+ import os
3
+
1
4
  from ._internal._models import (
2
5
  Artifact,
3
6
  ArtifactData,
@@ -16,7 +19,8 @@ from ._internal._models import (
16
19
  )
17
20
  from ._internal._enums import (
18
21
  BuildStatus,
19
- TaskEndpointStatus
22
+ TaskEndpointStatus,
23
+ TaskStatus
20
24
  )
21
25
  from .client import Client
22
26
 
@@ -39,3 +43,10 @@ __all__ = [
39
43
  "BuildStatus",
40
44
  "TaskEndpointStatus",
41
45
  ]
46
+
47
+ # Configure logging
48
+ log_level = os.getenv("GMI_CLOUD_LOG_LEVEL", "INFO").upper()
49
+ logging.basicConfig(
50
+ level=log_level,
51
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
52
+ )
@@ -1,4 +1,6 @@
1
1
  from typing import List
2
+ import logging
3
+ from requests.exceptions import RequestException
2
4
 
3
5
  from ._http_client import HTTPClient
4
6
  from ._iam_client import IAMClient
@@ -6,6 +8,8 @@ from ._decorator import handle_refresh_token
6
8
  from .._models import *
7
9
  from .._config import ARTIFACT_SERVICE_BASE_URL
8
10
 
11
+ logger = logging.getLogger(__name__)
12
+
9
13
 
10
14
  class ArtifactClient:
11
15
  """
@@ -24,119 +28,185 @@ class ArtifactClient:
24
28
  self.iam_client = iam_client
25
29
 
26
30
  @handle_refresh_token
27
- def get_artifact(self, artifact_id: str) -> Artifact:
31
+ def get_artifact(self, artifact_id: str) -> Optional[Artifact]:
28
32
  """
29
33
  Fetches an artifact by its ID.
30
34
 
31
35
  :param artifact_id: The ID of the artifact to fetch.
32
- :return: The Artifact object.
33
- :rtype: Artifact
34
- """
35
- result = self.client.get(f"/get_artifact", self.iam_client.get_custom_headers(), {"artifact_id": artifact_id})
36
-
37
- return Artifact.model_validate(result)
36
+ :return: The Artifact object or None if an error occurs.
37
+ """
38
+ try:
39
+ response = self.client.get(
40
+ "/get_artifact",
41
+ self.iam_client.get_custom_headers(),
42
+ {"artifact_id": artifact_id}
43
+ )
44
+ return Artifact.model_validate(response) if response else None
45
+ except (RequestException, ValueError) as e:
46
+ logger.error(f"Failed to fetch artifact {artifact_id}: {e}")
47
+ return None
38
48
 
39
49
  @handle_refresh_token
40
50
  def get_all_artifacts(self) -> List[Artifact]:
41
51
  """
42
52
  Fetches all artifacts.
43
53
 
44
- :return: A list of Artifact objects.
45
- :rtype: List[Artifact]
54
+ :return: A list of Artifact objects. If an error occurs, returns an empty list.
46
55
  """
47
- result = self.client.get("/get_all_artifacts", self.iam_client.get_custom_headers())
48
- if not result:
56
+ try:
57
+ response = self.client.get("/get_all_artifacts", self.iam_client.get_custom_headers())
58
+ if not response:
59
+ logger.error("Empty response from /get_all_artifacts")
60
+ return []
61
+ return [Artifact.model_validate(item) for item in response]
62
+ except (RequestException, ValueError) as e:
63
+ logger.error(f"Failed to fetch all artifacts: {e}")
49
64
  return []
50
- return [Artifact.model_validate(item) for item in result]
51
65
 
52
66
  @handle_refresh_token
53
- def create_artifact(self, request: CreateArtifactRequest) -> CreateArtifactResponse:
67
+ def create_artifact(self, request: CreateArtifactRequest) -> Optional[CreateArtifactResponse]:
54
68
  """
55
69
  Creates a new artifact in the service.
56
70
 
57
71
  :param request: The request object containing artifact details.
58
- :return: The response object containing the created artifact details.
59
- :rtype: CreateArtifactResponse
60
- """
61
- result = self.client.post("/create_artifact", self.iam_client.get_custom_headers(), request.model_dump())
62
-
63
- return CreateArtifactResponse.model_validate(result)
72
+ :return: The response object containing the created artifact details, or None on error.
73
+ """
74
+ try:
75
+ response = self.client.post(
76
+ "/create_artifact",
77
+ self.iam_client.get_custom_headers(),
78
+ request.model_dump()
79
+ )
80
+ return CreateArtifactResponse.model_validate(response) if response else None
81
+ except (RequestException, ValueError) as e:
82
+ logger.error(f"Failed to create artifact: {e}")
83
+ return None
64
84
 
65
85
  @handle_refresh_token
66
- def create_artifact_from_template(self, artifact_template_id: str) -> CreateArtifactFromTemplateResponse:
86
+ def create_artifact_from_template(self, artifact_template_id: str) -> Optional[CreateArtifactFromTemplateResponse]:
67
87
  """
68
88
  Creates a new artifact in the service.
69
89
 
70
90
  :param artifact_template_id: The ID of the artifact template to use.
71
- :return: The response object containing the created artifact details.
72
- :rtype: CreateArtifactFromTemplateResponse
73
- """
74
- result = self.client.post("/create_artifact_from_template", self.iam_client.get_custom_headers(),
75
- {"artifact_template_id": artifact_template_id})
76
-
77
- return CreateArtifactFromTemplateResponse.model_validate(result)
91
+ :return: The response object containing the created artifact details or None if an error occurs.
92
+ """
93
+ try:
94
+ response = self.client.post(
95
+ "/create_artifact_from_template",
96
+ self.iam_client.get_custom_headers(),
97
+ {"artifact_template_id": artifact_template_id}
98
+ )
99
+ return CreateArtifactFromTemplateResponse.model_validate(response) if response else None
100
+ except (RequestException, ValueError) as e:
101
+ logger.error(f"Failed to create artifact from template {artifact_template_id}: {e}")
102
+ return None
78
103
 
79
104
  @handle_refresh_token
80
- def rebuild_artifact(self, artifact_id: str) -> RebuildArtifactResponse:
105
+ def rebuild_artifact(self, artifact_id: str) -> Optional[RebuildArtifactResponse]:
81
106
  """
82
107
  Rebuilds an artifact in the service.
83
108
 
84
109
  :param artifact_id: The ID of the artifact to rebuild.
85
- :return: The response object containing the rebuilt artifact details.
86
- :rtype: RebuildArtifactResponse
87
- """
88
- result = self.client.post("/rebuild_artifact", self.iam_client.get_custom_headers(),
89
- {"artifact_id": artifact_id})
90
-
91
- return CreateArtifactResponse.model_validate(result)
110
+ :return: The response object containing the rebuilt artifact details or None if an error occurs.
111
+ """
112
+ try:
113
+ response = self.client.post(
114
+ "/rebuild_artifact",
115
+ self.iam_client.get_custom_headers(),
116
+ {"artifact_id": artifact_id}
117
+ )
118
+ return RebuildArtifactResponse.model_validate(response) if response else None
119
+ except (RequestException, ValueError) as e:
120
+ logger.error(f"Failed to rebuild artifact {artifact_id}: {e}")
121
+ return None
92
122
 
93
123
  @handle_refresh_token
94
- def delete_artifact(self, artifact_id: str) -> DeleteArtifactResponse:
124
+ def delete_artifact(self, artifact_id: str) -> Optional[DeleteArtifactResponse]:
95
125
  """
96
126
  Deletes an artifact by its ID.
97
127
 
98
128
  :param artifact_id: The ID of the artifact to delete.
99
- :return: The response object containing the deleted artifact details.
100
- :rtype: DeleteArtifactResponse
101
- """
102
- result = self.client.delete("/delete_artifact", self.iam_client.get_custom_headers(),
103
- {"artifact_id": artifact_id})
104
-
105
- return DeleteArtifactResponse.model_validate(result)
129
+ :return: The response object containing the deleted artifact details or None if an error occurs.
130
+ """
131
+ try:
132
+ response = self.client.delete(
133
+ "/delete_artifact",
134
+ self.iam_client.get_custom_headers(),
135
+ {"artifact_id": artifact_id}
136
+ )
137
+ return DeleteArtifactResponse.model_validate(response) if response else None
138
+ except (RequestException, ValueError) as e:
139
+ logger.error(f"Failed to delete artifact {artifact_id}: {e}")
140
+ return None
106
141
 
107
142
  @handle_refresh_token
108
- def get_bigfile_upload_url(self, request: GetBigFileUploadUrlRequest) -> GetBigFileUploadUrlResponse:
143
+ def get_bigfile_upload_url(self, request: GetBigFileUploadUrlRequest) -> Optional[GetBigFileUploadUrlResponse]:
109
144
  """
110
145
  Generates a pre-signed URL for uploading a large file.
111
146
 
112
147
  :param request: The request object containing the artifact ID, file name, and file type.
113
- :return: The response object containing the pre-signed URL and upload details.
114
- :rtype: GetBigFileUploadUrlResponse
148
+ :return: The response object containing the pre-signed URL and upload details, or None if an error occurs.
115
149
  """
116
- result = self.client.post("/get_bigfile_upload_url", self.iam_client.get_custom_headers(), request.model_dump())
150
+ try:
151
+ response = self.client.post("/get_bigfile_upload_url",
152
+ self.iam_client.get_custom_headers(),
153
+ request.model_dump())
154
+
155
+ if not response:
156
+ logger.error("Empty response from /get_bigfile_upload_url")
157
+ return None
158
+
159
+ return GetBigFileUploadUrlResponse.model_validate(response)
117
160
 
118
- return GetBigFileUploadUrlResponse.model_validate(result)
161
+ except (RequestException, ValueError) as e:
162
+ logger.error(f"Failed to generate upload URL: {e}")
163
+ return None
119
164
 
120
165
  @handle_refresh_token
121
- def delete_bigfile(self, request: DeleteBigfileRequest) -> DeleteBigfileResponse:
166
+ def delete_bigfile(self, request: DeleteBigfileRequest) -> Optional[DeleteBigfileResponse]:
122
167
  """
123
168
  Deletes a large file associated with an artifact.
124
169
 
125
170
  :param request: The request object containing the artifact ID and file name.
126
- :return: The response object containing the deletion status.
127
- :rtype: DeleteBigfileResponse
171
+ :return: The response object containing the deletion status, or None if an error occurs.
128
172
  """
129
- result = self.client.delete("/delete_bigfile", self.iam_client.get_custom_headers(), request.dict())
173
+ try:
174
+ response = self.client.delete("/delete_bigfile",
175
+ self.iam_client.get_custom_headers(),
176
+ request.model_dump())
177
+
178
+ if not response:
179
+ logger.error("Empty response from /delete_bigfile")
180
+ return None
181
+
182
+ return DeleteBigfileResponse.model_validate(response)
130
183
 
131
- return DeleteBigfileResponse.model_validate(result)
184
+ except (RequestException, ValueError) as e:
185
+ logger.error(f"Failed to delete big file: {e}")
186
+ return None
132
187
 
133
188
  @handle_refresh_token
134
- def get_artifact_templates(self) -> GetArtifactTemplatesResponse:
189
+ def get_public_templates(self) -> List[ArtifactTemplate]:
135
190
  """
136
191
  Fetches all artifact templates.
137
192
 
138
193
  :return: A list of ArtifactTemplate objects.
139
194
  :rtype: List[ArtifactTemplate]
140
195
  """
141
- result = self.client.get("/get_artifact_templates", self.iam_client.get_custom_headers())
142
- return GetArtifactTemplatesResponse.model_validate(result)
196
+ try:
197
+ response = self.client.get("/get_public_templates", self.iam_client.get_custom_headers())
198
+
199
+ if not response:
200
+ logger.error("Empty response received from /get_public_templates API")
201
+ return []
202
+
203
+ try:
204
+ result = GetPublicTemplatesResponse.model_validate(response)
205
+ return result.artifact_templates
206
+ except ValueError as ve:
207
+ logger.error(f"Failed to validate response data: {ve}")
208
+ return []
209
+
210
+ except RequestException as e:
211
+ logger.error(f"Request to /get_public_templates failed: {e}")
212
+ return []
@@ -1,8 +1,11 @@
1
+ import logging
2
+
1
3
  import requests
2
4
  from .._exceptions import APIError
3
5
  from .._exceptions import UnauthorizedError
4
6
  from .._constants import *
5
- from .._config import *
7
+
8
+ logger = logging.getLogger(__name__)
6
9
 
7
10
 
8
11
  class HTTPClient:
@@ -52,6 +55,7 @@ class HTTPClient:
52
55
  response = None
53
56
  try:
54
57
  response = requests.request(method, url, params=params, json=data, headers=headers)
58
+ logger.debug(response.text)
55
59
  if response.status_code == 401:
56
60
  raise UnauthorizedError("Access token expired or invalid.")
57
61
  elif response.status_code != 200 and response.status_code != 201:
@@ -62,7 +66,6 @@ class HTTPClient:
62
66
  raise APIError(f"HTTP Request failed: {error_message}")
63
67
  # Raise for HTTP errors
64
68
  response.raise_for_status()
65
- print(response.text)
66
69
 
67
70
  except requests.exceptions.RequestException as e:
68
71
  raise APIError(f"HTTP Request failed: {str(e)}")
@@ -1,10 +1,14 @@
1
1
  import jwt
2
+ import logging
3
+ from requests.exceptions import RequestException
2
4
 
3
5
  from ._http_client import HTTPClient
4
6
  from .._config import IAM_SERVICE_BASE_URL
5
7
  from .._models import *
6
8
  from .._constants import CLIENT_ID_HEADER, AUTHORIZATION_HEADER
7
9
 
10
+ logger = logging.getLogger(__name__)
11
+
8
12
 
9
13
  class IAMClient:
10
14
  """
@@ -25,35 +29,113 @@ class IAMClient:
25
29
  self._access_token = ""
26
30
  self._refresh_token = ""
27
31
  self._user_id = ""
32
+ self._organization_id = ""
28
33
  self.client = HTTPClient(IAM_SERVICE_BASE_URL)
29
34
 
30
- def login(self):
35
+ def login(self) -> bool:
31
36
  """
32
- Logs in a user with the given username and password.
37
+ Logs in a user with the given email and password.
38
+ Returns True if login is successful, otherwise False.
33
39
  """
34
- custom_headers = {
35
- CLIENT_ID_HEADER: self._client_id
36
- }
37
- req = LoginRequest(email=self._email, password=self._password)
38
- result = self.client.post("/me/sessions", custom_headers, req.model_dump())
40
+ try:
41
+ custom_headers = {CLIENT_ID_HEADER: self._client_id}
42
+ req = AuthTokenRequest(email=self._email, password=self._password)
43
+ auth_tokens_result = self.client.post("/me/auth-tokens", custom_headers, req.model_dump())
44
+
45
+ if not auth_tokens_result:
46
+ logger.error("Login failed: Received empty response from auth-tokens endpoint")
47
+ return False
48
+
49
+ auth_tokens_resp = AuthTokenResponse.model_validate(auth_tokens_result)
50
+
51
+ # Handle 2FA
52
+ if auth_tokens_resp.is2FARequired:
53
+ for attempt in range(3):
54
+ code = input(f"Attempt {attempt + 1}/3: Please enter the 2FA code: ")
55
+ create_session_req = CreateSessionRequest(
56
+ type="native", authToken=auth_tokens_resp.authToken, otpCode=code
57
+ )
58
+ try:
59
+ session_result = self.client.post("/me/sessions", custom_headers,
60
+ create_session_req.model_dump())
61
+ if session_result:
62
+ break
63
+ except RequestException:
64
+ logger.warning("Invalid 2FA code, please try again.")
65
+ if attempt == 2:
66
+ logger.error("Failed to create session after 3 incorrect 2FA attempts.")
67
+ return False
68
+ else:
69
+ create_session_req = CreateSessionRequest(type="native", authToken=auth_tokens_resp.authToken,
70
+ otpCode=None)
71
+ session_result = self.client.post("/me/sessions", custom_headers, create_session_req.model_dump())
72
+
73
+ create_session_resp = CreateSessionResponse.model_validate(session_result)
39
74
 
40
- resp = LoginResponse.model_validate(result)
41
- self._access_token = resp.accessToken
42
- self._refresh_token = resp.refreshToken
43
- self._user_id = self.parse_user_id()
75
+ self._access_token = create_session_resp.accessToken
76
+ self._refresh_token = create_session_resp.refreshToken
77
+ self._user_id = self.parse_user_id()
44
78
 
45
- def refresh_token(self):
79
+ # Fetch profile to get organization ID
80
+ profile_result = self.client.get("/me/profile", self.get_custom_headers())
81
+ if not profile_result:
82
+ logger.error("Failed to fetch user profile data.")
83
+ return False
84
+
85
+ profile_resp = ProfileResponse.model_validate(profile_result)
86
+ self._organization_id = profile_resp.organization.id
87
+
88
+ return True
89
+ except (RequestException, ValueError, KeyError) as e:
90
+ logger.error(f"Login failed due to exception: {e}")
91
+ return False
92
+
93
+ def refresh_token(self) -> bool:
46
94
  """
47
- Refreshes a user's token using a refresh token.
95
+ Refreshes the access token. Returns True on success, False otherwise.
48
96
  """
49
- custom_headers = {
50
- CLIENT_ID_HEADER: self._client_id
51
- }
52
- result = self.client.patch("/me/sessions", custom_headers, {"refreshToken": self._refresh_token})
97
+ try:
98
+ custom_headers = {CLIENT_ID_HEADER: self._client_id}
99
+ result = self.client.patch("/me/sessions", custom_headers, {"refreshToken": self._refresh_token})
100
+
101
+ if not result:
102
+ logger.error("Failed to refresh token: Empty response received")
103
+ return False
53
104
 
54
- resp = LoginResponse.model_validate(result)
55
- self._access_token = resp.accessToken
56
- self._refresh_token = resp.refreshToken
105
+ resp = CreateSessionResponse.model_validate(result)
106
+ self._access_token = resp.accessToken
107
+ self._refresh_token = resp.refreshToken
108
+
109
+ return True
110
+ except (RequestException, ValueError) as e:
111
+ logger.error(f"Token refresh failed: {e}")
112
+ return False
113
+
114
+ def create_org_api_key(self, request: CreateAPIKeyRequest) -> Optional[str]:
115
+ """
116
+ Creates a new API key for the current user.
117
+ """
118
+ try:
119
+ result = self.client.post(f"/organizations/{self.get_organization_id()}/api-keys",
120
+ self.get_custom_headers(), request.model_dump())
121
+
122
+ return CreateAPIKeyResponse.model_validate(result).key if result else None
123
+ except (RequestException, ValueError) as e:
124
+ logger.error(f"Failed to create API key: {e}")
125
+ return None
126
+
127
+ def get_org_api_keys(self) -> Optional[GetAPIKeysResponse]:
128
+ """
129
+ Fetches all API keys for the current user.
130
+ """
131
+ try:
132
+ result = self.client.get(f"/organizations/{self.get_organization_id()}/api-keys",
133
+ self.get_custom_headers())
134
+
135
+ return GetAPIKeysResponse.model_validate(result) if result else None
136
+ except (RequestException, ValueError) as e:
137
+ logger.error(f"Failed to retrieve organization API keys: {e}")
138
+ return None
57
139
 
58
140
  def parse_user_id(self) -> str:
59
141
  """
@@ -91,6 +173,12 @@ class IAMClient:
91
173
  """
92
174
  return self._client_id
93
175
 
176
+ def get_organization_id(self) -> str:
177
+ """
178
+ Gets the current organization ID.
179
+ """
180
+ return self._organization_id
181
+
94
182
  def get_custom_headers(self) -> dict:
95
183
  """
96
184
  Gets the custom headers for the IAM client.
@@ -1,9 +1,14 @@
1
+ import logging
2
+ from requests.exceptions import RequestException
3
+
1
4
  from ._http_client import HTTPClient
2
5
  from ._decorator import handle_refresh_token
3
6
  from ._iam_client import IAMClient
4
7
  from .._config import TASK_SERVICE_BASE_URL
5
8
  from .._models import *
6
9
 
10
+ logger = logging.getLogger(__name__)
11
+
7
12
 
8
13
  class TaskClient:
9
14
  """
@@ -21,17 +26,19 @@ class TaskClient:
21
26
  self.iam_client = iam_client
22
27
 
23
28
  @handle_refresh_token
24
- def get_task(self, task_id: str) -> Task:
29
+ def get_task(self, task_id: str) -> Optional[Task]:
25
30
  """
26
31
  Retrieves a task from the task service using the given task ID.
27
32
 
28
33
  :param task_id: The ID of the task to be retrieved.
29
- :return: An instance of Task containing the details of the retrieved task.
30
- :rtype: Task
34
+ :return: An instance of Task containing the details of the retrieved task, or None if an error occurs.
31
35
  """
32
- result = self.client.get("/get_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
33
-
34
- return Task.model_validate(result)
36
+ try:
37
+ response = self.client.get("/get_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
38
+ return Task.model_validate(response) if response else None
39
+ except (RequestException, ValueError) as e:
40
+ logger.error(f"Failed to retrieve task {task_id}: {e}")
41
+ return None
35
42
 
36
43
  @handle_refresh_token
37
44
  def get_all_tasks(self) -> GetAllTasksResponse:
@@ -39,70 +46,108 @@ class TaskClient:
39
46
  Retrieves all tasks from the task service.
40
47
 
41
48
  :return: An instance of GetAllTasksResponse containing the retrieved tasks.
42
- :rtype: GetAllTasksResponse
43
49
  """
44
- result = self.client.get("/get_tasks", self.iam_client.get_custom_headers())
45
- if not result:
50
+ try:
51
+ response = self.client.get("/get_tasks", self.iam_client.get_custom_headers())
52
+ if not response:
53
+ logger.error("Empty response from /get_tasks")
54
+ return GetAllTasksResponse(tasks=[])
55
+ return GetAllTasksResponse.model_validate(response)
56
+ except (RequestException, ValueError) as e:
57
+ logger.error(f"Failed to retrieve all tasks: {e}")
46
58
  return GetAllTasksResponse(tasks=[])
47
59
 
48
- return GetAllTasksResponse.model_validate(result)
49
-
50
60
  @handle_refresh_token
51
- def create_task(self, task: Task) -> CreateTaskResponse:
61
+ def create_task(self, task: Task) -> Optional[CreateTaskResponse]:
52
62
  """
53
63
  Creates a new task using the provided task object.
54
64
 
55
65
  :param task: The Task object containing the details of the task to be created.
66
+ :return: The response object containing created task details, or None if an error occurs.
56
67
  """
57
- result = self.client.post("/create_task", self.iam_client.get_custom_headers(), task.model_dump())
58
-
59
- return CreateTaskResponse.model_validate(result)
68
+ try:
69
+ response = self.client.post("/create_task", self.iam_client.get_custom_headers(), task.model_dump())
70
+ return CreateTaskResponse.model_validate(response) if response else None
71
+ except (RequestException, ValueError) as e:
72
+ logger.error(f"Failed to create task: {e}")
73
+ return None
60
74
 
61
75
  @handle_refresh_token
62
- def update_task_schedule(self, task: Task):
76
+ def update_task_schedule(self, task: Task) -> bool:
63
77
  """
64
78
  Updates the schedule of an existing task.
65
79
 
66
80
  :param task: The Task object containing the updated task details.
81
+ :return: True if update is successful, False otherwise.
67
82
  """
68
- self.client.put("/update_schedule", self.iam_client.get_custom_headers(), task.model_dump())
83
+ try:
84
+ response = self.client.put("/update_schedule", self.iam_client.get_custom_headers(), task.model_dump())
85
+ return response is not None
86
+ except RequestException as e:
87
+ logger.error(f"Failed to update schedule for task {task.task_id}: {e}")
88
+ return False
69
89
 
70
90
  @handle_refresh_token
71
- def start_task(self, task_id: str):
91
+ def start_task(self, task_id: str) -> bool:
72
92
  """
73
93
  Starts a task using the given task ID.
74
94
 
75
95
  :param task_id: The ID of the task to be started.
96
+ :return: True if start is successful, False otherwise.
76
97
  """
77
- self.client.post("/start_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
98
+ try:
99
+ response = self.client.post("/start_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
100
+ return response is not None
101
+ except RequestException as e:
102
+ logger.error(f"Failed to start task {task_id}: {e}")
103
+ return False
78
104
 
79
105
  @handle_refresh_token
80
- def stop_task(self, task_id: str):
106
+ def stop_task(self, task_id: str) -> bool:
81
107
  """
82
108
  Stops a running task using the given task ID.
83
109
 
84
110
  :param task_id: The ID of the task to be stopped.
111
+ :return: True if stop is successful, False otherwise.
85
112
  """
86
- self.client.post("/stop_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
113
+ try:
114
+ response = self.client.post("/stop_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
115
+ return response is not None
116
+ except RequestException as e:
117
+ logger.error(f"Failed to stop task {task_id}: {e}")
118
+ return False
87
119
 
88
120
  @handle_refresh_token
89
- def get_usage_data(self, start_timestamp: str, end_timestamp: str) -> GetUsageDataResponse:
121
+ def get_usage_data(self, start_timestamp: str, end_timestamp: str) -> Optional[GetUsageDataResponse]:
90
122
  """
91
123
  Retrieves the usage data of a task using the given task ID.
92
124
 
93
125
  :param start_timestamp: The start timestamp of the usage data.
94
126
  :param end_timestamp: The end timestamp of the usage data.
95
- """
96
- result = self.client.get("/get_usage_data", self.iam_client.get_custom_headers(),
97
- {"start_timestamp": start_timestamp, "end_timestamp": end_timestamp})
98
-
99
- return result
127
+ :return: An instance of GetUsageDataResponse, or None if an error occurs.
128
+ """
129
+ try:
130
+ response = self.client.get(
131
+ "/get_usage_data",
132
+ self.iam_client.get_custom_headers(),
133
+ {"start_timestamp": start_timestamp, "end_timestamp": end_timestamp}
134
+ )
135
+ return GetUsageDataResponse.model_validate(response) if response else None
136
+ except (RequestException, ValueError) as e:
137
+ logger.error(f"Failed to retrieve usage data from {start_timestamp} to {end_timestamp}: {e}")
138
+ return None
100
139
 
101
140
  @handle_refresh_token
102
- def archive_task(self, task_id: str):
141
+ def archive_task(self, task_id: str) -> bool:
103
142
  """
104
143
  Archives a task using the given task ID.
105
144
 
106
145
  :param task_id: The ID of the task to be archived.
107
- """
108
- self.client.post("/archive_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
146
+ :return: True if archiving is successful, False otherwise.
147
+ """
148
+ try:
149
+ response = self.client.post("/archive_task", self.iam_client.get_custom_headers(), {"task_id": task_id})
150
+ return response is not None
151
+ except RequestException as e:
152
+ logger.error(f"Failed to archive task {task_id}: {e}")
153
+ return False
@@ -23,3 +23,11 @@ class TaskEndpointStatus(str, Enum):
23
23
  READY = "ready"
24
24
  UNREADY = "unready"
25
25
  NEW = "new"
26
+
27
+ class TaskStatus(str, Enum):
28
+ IDLE = "idle"
29
+ STARTING = "starting"
30
+ IN_QUEUE = "in-queue"
31
+ RUNNING = "running"
32
+ NEEDSTOP = "needstop"
33
+ ARCHIVED = "archived"