|
6 | 6 | from datetime import datetime, timezone |
7 | 7 | from pathlib import Path |
8 | 8 | from urllib.parse import urlparse |
| 9 | +from uuid import UUID |
9 | 10 |
|
10 | 11 | import httpx |
11 | 12 | from loguru import logger |
|
19 | 20 | ExportFormat, |
20 | 21 | GithubTokenResponse, |
21 | 22 | MetricAggregationType, |
| 23 | + Organization, |
| 24 | + PaginatedWorkspaces, |
22 | 25 | Project, |
23 | 26 | RawRun, |
24 | 27 | RawTask, |
|
34 | 37 | TraceTree, |
35 | 38 | UserDataCredentials, |
36 | 39 | UserResponse, |
| 40 | + Workspace, |
| 41 | + WorkspaceFilter, |
37 | 42 | ) |
38 | 43 | from dreadnode.api.util import ( |
39 | 44 | convert_flat_tasks_to_tree, |
@@ -276,16 +281,47 @@ def list_projects(self) -> list[Project]: |
276 | 281 | response = self.request("GET", "/strikes/projects") |
277 | 282 | return [Project(**project) for project in response.json()] |
278 | 283 |
|
279 | | - def get_project(self, project: str) -> Project: |
| 284 | + def get_project(self, project_identifier: str | UUID, workspace_id: UUID) -> Project: |
280 | 285 | """Retrieves details of a specific project. |
281 | 286 |
|
282 | 287 | Args: |
283 | | - project (str): The project identifier. |
| 288 | + project (str | UUID): The project identifier. ID, name, or slug. |
284 | 289 |
|
285 | 290 | Returns: |
286 | 291 | Project: The Project object. |
287 | 292 | """ |
288 | | - response = self.request("GET", f"/strikes/projects/{project!s}") |
| 293 | + response = self.request( |
| 294 | + "GET", |
| 295 | + f"/strikes/projects/{project_identifier!s}", |
| 296 | + params={"workspace_id": workspace_id}, |
| 297 | + ) |
| 298 | + return Project(**response.json()) |
| 299 | + |
| 300 | + def create_project( |
| 301 | + self, |
| 302 | + name: str | UUID | None = None, |
| 303 | + workspace_id: UUID | None = None, |
| 304 | + organization_id: UUID | None = None, |
| 305 | + ) -> Project: |
| 306 | + """Creates a new project. |
| 307 | +
|
| 308 | + Args: |
| 309 | + name (str | UUID | None): The name of the project. If None, a default name will be used. |
| 310 | + workspace (str | UUID | None): The workspace ID to create the project in. If None, the default workspace will be used. |
| 311 | + organization (str | UUID | None): The organization ID to create the project in. If None, the default organization will be used. |
| 312 | +
|
| 313 | + Returns: |
| 314 | + Project: The created Project object. |
| 315 | + """ |
| 316 | + payload: dict[str, t.Any] = {} |
| 317 | + if name is not None: |
| 318 | + payload["name"] = name |
| 319 | + if workspace_id is not None: |
| 320 | + payload["workspace_id"] = str(workspace_id) |
| 321 | + if organization_id is not None: |
| 322 | + payload["org_id"] = str(organization_id) |
| 323 | + |
| 324 | + response = self.request("POST", "/strikes/projects", json_data=payload) |
289 | 325 | return Project(**response.json()) |
290 | 326 |
|
291 | 327 | def list_runs(self, project: str) -> list[RunSummary]: |
@@ -757,3 +793,97 @@ def get_platform_templates(self, tag: str) -> bytes: |
757 | 793 | response = self.request("GET", "/platform/templates/all", params=params) |
758 | 794 | zip_content: bytes = response.content |
759 | 795 | return zip_content |
| 796 | + |
| 797 | + # RBAC |
| 798 | + def list_organizations(self) -> list[Organization]: |
| 799 | + """ |
| 800 | + Retrieves a list of organizations the user belongs to. |
| 801 | +
|
| 802 | + Returns: |
| 803 | + A list of organization names. |
| 804 | + """ |
| 805 | + response = self.request("GET", "/organizations") |
| 806 | + return [Organization(**org) for org in response.json()] |
| 807 | + |
| 808 | + def get_organization(self, organization_id: str | UUID) -> Organization: |
| 809 | + """ |
| 810 | + Retrieves details of a specific organization. |
| 811 | +
|
| 812 | + Args: |
| 813 | + organization_id (str): The organization identifier. |
| 814 | +
|
| 815 | + Returns: |
| 816 | + Organization: The Organization object. |
| 817 | + """ |
| 818 | + response = self.request("GET", f"/organizations/{organization_id!s}") |
| 819 | + return Organization(**response.json()) |
| 820 | + |
| 821 | + def list_workspaces(self, filters: WorkspaceFilter | None = None) -> list[Workspace]: |
| 822 | + """ |
| 823 | + Retrieves a list of workspaces the user has access to. |
| 824 | +
|
| 825 | + Returns: |
| 826 | + A list of workspace names. |
| 827 | + """ |
| 828 | + response = self.request( |
| 829 | + "GET", "/workspaces", params=filters.model_dump() if filters else None |
| 830 | + ) |
| 831 | + paginated_workspaces = PaginatedWorkspaces(**response.json()) |
| 832 | + # handle the pagination |
| 833 | + all_workspaces: list[Workspace] = paginated_workspaces.workspaces.copy() |
| 834 | + while paginated_workspaces.has_next: |
| 835 | + response = self.request( |
| 836 | + "GET", |
| 837 | + "/workspaces", |
| 838 | + params={ |
| 839 | + "page": paginated_workspaces.page + 1, |
| 840 | + "limit": paginated_workspaces.limit, |
| 841 | + **(filters.model_dump() if filters else {}), |
| 842 | + }, |
| 843 | + ) |
| 844 | + next_page = PaginatedWorkspaces(**response.json()) |
| 845 | + all_workspaces.extend(next_page.workspaces) |
| 846 | + paginated_workspaces.page = next_page.page |
| 847 | + paginated_workspaces.has_next = next_page.has_next |
| 848 | + |
| 849 | + return all_workspaces |
| 850 | + |
| 851 | + def get_workspace(self, workspace_id: str | UUID, org_id: UUID | None = None) -> Workspace: |
| 852 | + """ |
| 853 | + Retrieves details of a specific workspace. |
| 854 | +
|
| 855 | + Args: |
| 856 | + workspace_id (str): The workspace identifier. |
| 857 | +
|
| 858 | + Returns: |
| 859 | + Workspace: The Workspace object. |
| 860 | + """ |
| 861 | + params: dict[str, str] = {} |
| 862 | + if org_id: |
| 863 | + params = {"org_id": str(org_id)} |
| 864 | + response = self.request("GET", f"/workspaces/{workspace_id!s}", params=params) |
| 865 | + return Workspace(**response.json()) |
| 866 | + |
| 867 | + def create_workspace( |
| 868 | + self, |
| 869 | + name: str, |
| 870 | + organization_id: UUID, |
| 871 | + ) -> Workspace: |
| 872 | + """ |
| 873 | + Creates a new workspace. |
| 874 | +
|
| 875 | + Args: |
| 876 | + name (str): The name of the workspace. |
| 877 | + organization_id (str | UUID): The organization ID to create the workspace in. |
| 878 | +
|
| 879 | + Returns: |
| 880 | + Workspace: The created Workspace object. |
| 881 | + """ |
| 882 | + |
| 883 | + payload = { |
| 884 | + "name": name, |
| 885 | + "org_id": str(organization_id), |
| 886 | + } |
| 887 | + |
| 888 | + response = self.request("POST", "/workspaces", json_data=payload) |
| 889 | + return Workspace(**response.json()) |
0 commit comments