From e3d75a1a5e0536b8d7589191ea99e752bf3b7d53 Mon Sep 17 00:00:00 2001 From: Mark Kim Date: Wed, 2 Sep 2026 12:33:41 -0400 Subject: [PATCH 1/3] feat(projects): add list metadata expansions --- .../_generated/api/projects/list_projects.py | 41 +++++- src/volcano_sdk/_generated/models/__init__.py | 8 +- ...auth_provider_api_response_200_provider.py | 10 -- .../models/list_projects_include_item.py | 10 ++ src/volcano_sdk/_generated/models/project.py | 45 +++++++ .../models/project_git_connection_summary.py | 121 ++++++++++++++++++ .../models/project_health_summary.py | 82 ++++++++++++ 7 files changed, 301 insertions(+), 16 deletions(-) delete mode 100644 src/volcano_sdk/_generated/models/call_o_auth_provider_api_response_200_provider.py create mode 100644 src/volcano_sdk/_generated/models/list_projects_include_item.py create mode 100644 src/volcano_sdk/_generated/models/project_git_connection_summary.py create mode 100644 src/volcano_sdk/_generated/models/project_health_summary.py diff --git a/src/volcano_sdk/_generated/api/projects/list_projects.py b/src/volcano_sdk/_generated/api/projects/list_projects.py index 33ff9e47..7239db10 100644 --- a/src/volcano_sdk/_generated/api/projects/list_projects.py +++ b/src/volcano_sdk/_generated/api/projects/list_projects.py @@ -9,6 +9,8 @@ from ... import errors from ...models.error import Error +from ...models.list_projects_include_item import check_list_projects_include_item +from ...models.list_projects_include_item import ListProjectsIncludeItem from ...models.paginated_projects import PaginatedProjects from ...types import UNSET, Unset from typing import cast @@ -23,6 +25,7 @@ def _get_kwargs( ending_before: str | Unset = UNSET, offset: int | Unset = 0, search: str | Unset = UNSET, + include: list[ListProjectsIncludeItem] | Unset = UNSET, ) -> dict[str, Any]: @@ -43,6 +46,16 @@ def _get_kwargs( params["search"] = search + json_include: list[str] | Unset = UNSET + if not isinstance(include, Unset): + json_include = [] + for include_item_data in include: + include_item: str = include_item_data + json_include.append(include_item) + + + params["include"] = json_include + params = {k: v for k, v in params.items() if v is not UNSET and v is not None} @@ -97,6 +110,7 @@ def sync_detailed( ending_before: str | Unset = UNSET, offset: int | Unset = 0, search: str | Unset = UNSET, + include: list[ListProjectsIncludeItem] | Unset = UNSET, ) -> Response[Error | PaginatedProjects]: """ List all projects for authenticated user @@ -107,7 +121,9 @@ def sync_detailed( `limit`, returns `next_cursor`/`prev_cursor`, and supports a bounded `offset` past the cursor anchor. Supplying `limit` without `page` selects cursor mode. `search` applies a case-insensitive project-name - filter in either mode. Sending `page` with `cursor` or `ending_before`, + filter in either mode. `include` optionally expands each returned + project with its Git connection and/or aggregate health summary using + `git_connection` and `health`. Sending `page` with `cursor` or `ending_before`, or sending both cursor directions, returns 400. Args: @@ -117,6 +133,7 @@ def sync_detailed( ending_before (str | Unset): offset (int | Unset): Default: 0. search (str | Unset): + include (list[ListProjectsIncludeItem] | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -134,6 +151,7 @@ def sync_detailed( ending_before=ending_before, offset=offset, search=search, +include=include, ) @@ -152,6 +170,7 @@ def sync( ending_before: str | Unset = UNSET, offset: int | Unset = 0, search: str | Unset = UNSET, + include: list[ListProjectsIncludeItem] | Unset = UNSET, ) -> Error | PaginatedProjects | None: """ List all projects for authenticated user @@ -162,7 +181,9 @@ def sync( `limit`, returns `next_cursor`/`prev_cursor`, and supports a bounded `offset` past the cursor anchor. Supplying `limit` without `page` selects cursor mode. `search` applies a case-insensitive project-name - filter in either mode. Sending `page` with `cursor` or `ending_before`, + filter in either mode. `include` optionally expands each returned + project with its Git connection and/or aggregate health summary using + `git_connection` and `health`. Sending `page` with `cursor` or `ending_before`, or sending both cursor directions, returns 400. Args: @@ -172,6 +193,7 @@ def sync( ending_before (str | Unset): offset (int | Unset): Default: 0. search (str | Unset): + include (list[ListProjectsIncludeItem] | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -190,6 +212,7 @@ def sync( ending_before=ending_before, offset=offset, search=search, +include=include, ).parsed @@ -202,6 +225,7 @@ async def asyncio_detailed( ending_before: str | Unset = UNSET, offset: int | Unset = 0, search: str | Unset = UNSET, + include: list[ListProjectsIncludeItem] | Unset = UNSET, ) -> Response[Error | PaginatedProjects]: """ List all projects for authenticated user @@ -212,7 +236,9 @@ async def asyncio_detailed( `limit`, returns `next_cursor`/`prev_cursor`, and supports a bounded `offset` past the cursor anchor. Supplying `limit` without `page` selects cursor mode. `search` applies a case-insensitive project-name - filter in either mode. Sending `page` with `cursor` or `ending_before`, + filter in either mode. `include` optionally expands each returned + project with its Git connection and/or aggregate health summary using + `git_connection` and `health`. Sending `page` with `cursor` or `ending_before`, or sending both cursor directions, returns 400. Args: @@ -222,6 +248,7 @@ async def asyncio_detailed( ending_before (str | Unset): offset (int | Unset): Default: 0. search (str | Unset): + include (list[ListProjectsIncludeItem] | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -239,6 +266,7 @@ async def asyncio_detailed( ending_before=ending_before, offset=offset, search=search, +include=include, ) @@ -257,6 +285,7 @@ async def asyncio( ending_before: str | Unset = UNSET, offset: int | Unset = 0, search: str | Unset = UNSET, + include: list[ListProjectsIncludeItem] | Unset = UNSET, ) -> Error | PaginatedProjects | None: """ List all projects for authenticated user @@ -267,7 +296,9 @@ async def asyncio( `limit`, returns `next_cursor`/`prev_cursor`, and supports a bounded `offset` past the cursor anchor. Supplying `limit` without `page` selects cursor mode. `search` applies a case-insensitive project-name - filter in either mode. Sending `page` with `cursor` or `ending_before`, + filter in either mode. `include` optionally expands each returned + project with its Git connection and/or aggregate health summary using + `git_connection` and `health`. Sending `page` with `cursor` or `ending_before`, or sending both cursor directions, returns 400. Args: @@ -277,6 +308,7 @@ async def asyncio( ending_before (str | Unset): offset (int | Unset): Default: 0. search (str | Unset): + include (list[ListProjectsIncludeItem] | Unset): Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -295,5 +327,6 @@ async def asyncio( ending_before=ending_before, offset=offset, search=search, +include=include, )).parsed diff --git a/src/volcano_sdk/_generated/models/__init__.py b/src/volcano_sdk/_generated/models/__init__.py index 89074505..300dc022 100644 --- a/src/volcano_sdk/_generated/models/__init__.py +++ b/src/volcano_sdk/_generated/models/__init__.py @@ -101,7 +101,6 @@ from .call_o_auth_provider_api_body_method import CallOAuthProviderAPIBodyMethod from .call_o_auth_provider_api_provider import CallOAuthProviderAPIProvider from .call_o_auth_provider_api_response_200 import CallOAuthProviderAPIResponse200 -from .call_o_auth_provider_api_response_200_provider import CallOAuthProviderAPIResponse200Provider from .complete_upload_session_response import CompleteUploadSessionResponse from .configure_auth_methods_body import ConfigureAuthMethodsBody from .configure_auth_methods_body_oauth_providers_item import ConfigureAuthMethodsBodyOauthProvidersItem @@ -269,6 +268,7 @@ from .list_o_auth_configs_response_200 import ListOAuthConfigsResponse200 from .list_postgres_versions_response_200_item import ListPostgresVersionsResponse200Item from .list_project_deployments_resource_type import ListProjectDeploymentsResourceType +from .list_projects_include_item import ListProjectsIncludeItem from .list_storage_objects_admin_response_200 import ListStorageObjectsAdminResponse200 from .list_user_sessions_response_200 import ListUserSessionsResponse200 from .list_user_sessions_sort import ListUserSessionsSort @@ -381,6 +381,7 @@ from .project_frontend_custom_domain import ProjectFrontendCustomDomain from .project_frontend_custom_domain_frontend import ProjectFrontendCustomDomainFrontend from .project_git_connection import ProjectGitConnection +from .project_git_connection_summary import ProjectGitConnectionSummary from .project_git_deploy_settings import ProjectGitDeploySettings from .project_health_category import ProjectHealthCategory from .project_health_check import ProjectHealthCheck @@ -391,6 +392,7 @@ from .project_health_response import ProjectHealthResponse from .project_health_scope import ProjectHealthScope from .project_health_status import ProjectHealthStatus +from .project_health_summary import ProjectHealthSummary from .project_import_action import ProjectImportAction from .project_import_action_code import ProjectImportActionCode from .project_import_destination import ProjectImportDestination @@ -599,7 +601,6 @@ "CallOAuthProviderAPIBodyMethod", "CallOAuthProviderAPIProvider", "CallOAuthProviderAPIResponse200", - "CallOAuthProviderAPIResponse200Provider", "CompleteUploadSessionResponse", "ConfigureAuthMethodsBody", "ConfigureAuthMethodsBodyOauthProvidersItem", @@ -767,6 +768,7 @@ "ListOAuthConfigsResponse200", "ListPostgresVersionsResponse200Item", "ListProjectDeploymentsResourceType", + "ListProjectsIncludeItem", "ListStorageObjectsAdminResponse200", "ListUserSessionsResponse200", "ListUserSessionsSort", @@ -879,6 +881,7 @@ "ProjectFrontendCustomDomain", "ProjectFrontendCustomDomainFrontend", "ProjectGitConnection", + "ProjectGitConnectionSummary", "ProjectGitDeploySettings", "ProjectHealthCategory", "ProjectHealthCheck", @@ -889,6 +892,7 @@ "ProjectHealthResponse", "ProjectHealthScope", "ProjectHealthStatus", + "ProjectHealthSummary", "ProjectImportAction", "ProjectImportActionCode", "ProjectImportDestination", diff --git a/src/volcano_sdk/_generated/models/call_o_auth_provider_api_response_200_provider.py b/src/volcano_sdk/_generated/models/call_o_auth_provider_api_response_200_provider.py deleted file mode 100644 index 1a657f40..00000000 --- a/src/volcano_sdk/_generated/models/call_o_auth_provider_api_response_200_provider.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Literal - -CallOAuthProviderAPIResponse200Provider = Literal['apple', 'github', 'google', 'microsoft'] - -CALL_O_AUTH_PROVIDER_API_RESPONSE_200_PROVIDER_VALUES: set[CallOAuthProviderAPIResponse200Provider] = { 'apple', 'github', 'google', 'microsoft', } - -def check_call_o_auth_provider_api_response_200_provider(value: str) -> CallOAuthProviderAPIResponse200Provider: - if value in CALL_O_AUTH_PROVIDER_API_RESPONSE_200_PROVIDER_VALUES: - return value - raise TypeError(f"Unexpected value {value!r}. Expected one of {CALL_O_AUTH_PROVIDER_API_RESPONSE_200_PROVIDER_VALUES!r}") diff --git a/src/volcano_sdk/_generated/models/list_projects_include_item.py b/src/volcano_sdk/_generated/models/list_projects_include_item.py new file mode 100644 index 00000000..2155c75e --- /dev/null +++ b/src/volcano_sdk/_generated/models/list_projects_include_item.py @@ -0,0 +1,10 @@ +from typing import Literal + +ListProjectsIncludeItem = Literal['git_connection', 'health'] + +LIST_PROJECTS_INCLUDE_ITEM_VALUES: set[ListProjectsIncludeItem] = { 'git_connection', 'health', } + +def check_list_projects_include_item(value: str) -> ListProjectsIncludeItem: + if value in LIST_PROJECTS_INCLUDE_ITEM_VALUES: + return value + raise TypeError(f"Unexpected value {value!r}. Expected one of {LIST_PROJECTS_INCLUDE_ITEM_VALUES!r}") diff --git a/src/volcano_sdk/_generated/models/project.py b/src/volcano_sdk/_generated/models/project.py index 74a8bdbf..5ccbd43b 100644 --- a/src/volcano_sdk/_generated/models/project.py +++ b/src/volcano_sdk/_generated/models/project.py @@ -17,6 +17,9 @@ from uuid import UUID import datetime +if TYPE_CHECKING: + from ..models.project_git_connection_summary import ProjectGitConnectionSummary + from ..models.project_health_summary import ProjectHealthSummary @@ -47,6 +50,8 @@ class Project: param that changes on each upload. Absent when the project has no logo. The logo image is stored in the project's storage folder. Example: /projects/3fa85f64-5717-4562-b3fc-2c963f66afa6/logo?v=1718524800. + git_connection (ProjectGitConnectionSummary | Unset): + health (ProjectHealthSummary | Unset): """ id: UUID @@ -60,6 +65,8 @@ class Project: aws_application_name: str | Unset = UNSET last_invoked_at: datetime.datetime | Unset = UNSET logo_url: str | Unset = UNSET + git_connection: ProjectGitConnectionSummary | Unset = UNSET + health: ProjectHealthSummary | Unset = UNSET additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) @@ -67,6 +74,8 @@ class Project: def to_dict(self) -> dict[str, Any]: + from ..models.project_git_connection_summary import ProjectGitConnectionSummary + from ..models.project_health_summary import ProjectHealthSummary id = str(self.id) name = self.name @@ -96,6 +105,14 @@ def to_dict(self) -> dict[str, Any]: logo_url = self.logo_url + git_connection: dict[str, Any] | Unset = UNSET + if not isinstance(self.git_connection, Unset): + git_connection = self.git_connection.to_dict() + + health: dict[str, Any] | Unset = UNSET + if not isinstance(self.health, Unset): + health = self.health.to_dict() + field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) @@ -116,6 +133,10 @@ def to_dict(self) -> dict[str, Any]: field_dict["last_invoked_at"] = last_invoked_at if logo_url is not UNSET: field_dict["logo_url"] = logo_url + if git_connection is not UNSET: + field_dict["git_connection"] = git_connection + if health is not UNSET: + field_dict["health"] = health return field_dict @@ -123,6 +144,8 @@ def to_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + from ..models.project_git_connection_summary import ProjectGitConnectionSummary + from ..models.project_health_summary import ProjectHealthSummary d = dict(src_dict) id = UUID(d.pop("id")) @@ -175,6 +198,26 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: logo_url = d.pop("logo_url", UNSET) + _git_connection = d.pop("git_connection", UNSET) + git_connection: ProjectGitConnectionSummary | Unset + if isinstance(_git_connection, Unset): + git_connection = UNSET + else: + git_connection = ProjectGitConnectionSummary.from_dict(_git_connection) + + + + + _health = d.pop("health", UNSET) + health: ProjectHealthSummary | Unset + if isinstance(_health, Unset): + health = UNSET + else: + health = ProjectHealthSummary.from_dict(_health) + + + + project = cls( id=id, name=name, @@ -187,6 +230,8 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: aws_application_name=aws_application_name, last_invoked_at=last_invoked_at, logo_url=logo_url, + git_connection=git_connection, + health=health, ) diff --git a/src/volcano_sdk/_generated/models/project_git_connection_summary.py b/src/volcano_sdk/_generated/models/project_git_connection_summary.py new file mode 100644 index 00000000..746925ee --- /dev/null +++ b/src/volcano_sdk/_generated/models/project_git_connection_summary.py @@ -0,0 +1,121 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, BinaryIO, TextIO, TYPE_CHECKING, Generator + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +from typing import cast +import datetime + + + + + + +T = TypeVar("T", bound="ProjectGitConnectionSummary") + + + +@_attrs_define +class ProjectGitConnectionSummary: + """ + Attributes: + repo_installation_id (int): + repo_id (int): + repo_full_name (str): + root_directory (str): + production_branch (str): + updated_at (datetime.datetime): + """ + + repo_installation_id: int + repo_id: int + repo_full_name: str + root_directory: str + production_branch: str + updated_at: datetime.datetime + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + + + + + def to_dict(self) -> dict[str, Any]: + repo_installation_id = self.repo_installation_id + + repo_id = self.repo_id + + repo_full_name = self.repo_full_name + + root_directory = self.root_directory + + production_branch = self.production_branch + + updated_at = self.updated_at.isoformat() + + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "repo_installation_id": repo_installation_id, + "repo_id": repo_id, + "repo_full_name": repo_full_name, + "root_directory": root_directory, + "production_branch": production_branch, + "updated_at": updated_at, + }) + + return field_dict + + + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + repo_installation_id = d.pop("repo_installation_id") + + repo_id = d.pop("repo_id") + + repo_full_name = d.pop("repo_full_name") + + root_directory = d.pop("root_directory") + + production_branch = d.pop("production_branch") + + updated_at = datetime.datetime.fromisoformat(d.pop("updated_at")) + + + + + project_git_connection_summary = cls( + repo_installation_id=repo_installation_id, + repo_id=repo_id, + repo_full_name=repo_full_name, + root_directory=root_directory, + production_branch=production_branch, + updated_at=updated_at, + ) + + + project_git_connection_summary.additional_properties = d + return project_git_connection_summary + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties diff --git a/src/volcano_sdk/_generated/models/project_health_summary.py b/src/volcano_sdk/_generated/models/project_health_summary.py new file mode 100644 index 00000000..804a732f --- /dev/null +++ b/src/volcano_sdk/_generated/models/project_health_summary.py @@ -0,0 +1,82 @@ +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any, TypeVar, BinaryIO, TextIO, TYPE_CHECKING, Generator + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..types import UNSET, Unset + +from ..models.project_health_status import check_project_health_status +from ..models.project_health_status import ProjectHealthStatus +from typing import cast + + + + + + +T = TypeVar("T", bound="ProjectHealthSummary") + + + +@_attrs_define +class ProjectHealthSummary: + """ + Attributes: + status (ProjectHealthStatus): + """ + + status: ProjectHealthStatus + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + + + + + def to_dict(self) -> dict[str, Any]: + status: str = self.status + + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({ + "status": status, + }) + + return field_dict + + + + @classmethod + def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: + d = dict(src_dict) + status = check_project_health_status(d.pop("status")) + + + + + project_health_summary = cls( + status=status, + ) + + + project_health_summary.additional_properties = d + return project_health_summary + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties From eda1ee8b8ea218d1d3fb924bd394661a044c651c Mon Sep 17 00:00:00 2001 From: Mark Kim Date: Wed, 2 Sep 2026 12:39:17 -0400 Subject: [PATCH 2/3] fix(auth): handle raw OAuth provider responses --- src/volcano_sdk/auth.py | 2 +- tests/unit/test_state.py | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/volcano_sdk/auth.py b/src/volcano_sdk/auth.py index 95ff8a81..da9a9d3d 100644 --- a/src/volcano_sdk/auth.py +++ b/src/volcano_sdk/auth.py @@ -440,7 +440,7 @@ def _oauth_provider_token_status_from_payload( def _oauth_api_data_from_payload(payload: object) -> JSONValue: if not isinstance(payload, CallOAuthProviderAPIResponse200): raise VolcanoError(_INVALID_OAUTH_API_RESPONSE) - return _freeze_json(cast("JSONValue", payload.data)) + return _freeze_json(cast("JSONValue", payload.additional_properties)) class AuthContext(Protocol): diff --git a/tests/unit/test_state.py b/tests/unit/test_state.py index 5c4ce367..9135bc5a 100644 --- a/tests/unit/test_state.py +++ b/tests/unit/test_state.py @@ -316,14 +316,7 @@ def _configure_oauth(self) -> None: self.on_refresh_oauth_provider_token: Callable[[], None] | None = None self.call_oauth_api_response = Response( 200, - CallOAuthProviderAPIResponse200.from_dict( - { - "provider": "github", - "endpoint": "/user/repos", - "status_code": 200, - "data": [{"name": "volcano"}], - } - ), + CallOAuthProviderAPIResponse200.from_dict({"name": "volcano"}), ) self.call_oauth_api_calls: list[dict[str, Any]] = [] self.on_call_oauth_api: Callable[[], None] | None = None @@ -2300,7 +2293,7 @@ def test_call_oauth_api_returns_immutable_provider_data() -> None: body={"visibility": "private"}, ) - assert result == ({"name": "volcano"},) + assert result == {"name": "volcano"} assert client.auth.get_session() is established assert transport.call_oauth_api_calls == [ { @@ -2311,9 +2304,9 @@ def test_call_oauth_api_returns_immutable_provider_data() -> None: "body": {"visibility": "private"}, } ] - repos = cast("tuple[dict[str, object], ...]", result) + provider_data = cast("dict[str, object]", result) with pytest.raises(TypeError): - repos[0]["name"] = "changed" + provider_data["name"] = "changed" def test_call_oauth_api_rejects_an_unknown_provider() -> None: From 3b28560ce1aa52abdc6ae19767c2c756fb0a7733 Mon Sep 17 00:00:00 2001 From: Mark Kim Date: Mon, 14 Sep 2026 16:39:33 -0400 Subject: [PATCH 3/3] fix(openapi): regenerate project list expansions --- openapi/openapi.yaml | 58 ++++++++++++++++++- src/volcano_sdk/_generated/models/__init__.py | 2 + ...auth_provider_api_response_200_provider.py | 10 ++++ src/volcano_sdk/auth.py | 2 +- tests/unit/test_state.py | 15 +++-- 5 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 src/volcano_sdk/_generated/models/call_o_auth_provider_api_response_200_provider.py diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 22a2620f..0a25e86e 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -949,7 +949,9 @@ paths: `limit`, returns `next_cursor`/`prev_cursor`, and supports a bounded `offset` past the cursor anchor. Supplying `limit` without `page` selects cursor mode. `search` applies a case-insensitive project-name - filter in either mode. Sending `page` with `cursor` or `ending_before`, + filter in either mode. `include` optionally expands each returned + project with its Git connection and/or aggregate health summary using + `git_connection` and `health`. Sending `page` with `cursor` or `ending_before`, or sending both cursor directions, returns 400. operationId: listProjects security: @@ -961,6 +963,20 @@ paths: - $ref: '#/components/parameters/EndingBefore' - $ref: '#/components/parameters/Offset' - $ref: '#/components/parameters/Search' + - name: include + in: query + required: false + description: Optional comma-separated project metadata expansions. + style: form + explode: false + schema: + type: array + uniqueItems: true + items: + type: string + enum: + - git_connection + - health responses: '200': description: Successful response @@ -15597,6 +15613,14 @@ components: param that changes on each upload. Absent when the project has no logo. The logo image is stored in the project's storage folder. example: /projects/3fa85f64-5717-4562-b3fc-2c963f66afa6/logo?v=1718524800 + git_connection: + allOf: + - $ref: '#/components/schemas/ProjectGitConnectionSummary' + description: Present for connected projects when `git_connection` is requested through the list endpoint's `include` parameter. + health: + allOf: + - $ref: '#/components/schemas/ProjectHealthSummary' + description: Present when `health` is requested through the list endpoint's `include` parameter. created_at: type: string format: date-time @@ -15611,6 +15635,38 @@ components: - selected_regions - created_at - updated_at + ProjectGitConnectionSummary: + type: object + properties: + repo_installation_id: + type: integer + format: int64 + repo_id: + type: integer + format: int64 + repo_full_name: + type: string + root_directory: + type: string + production_branch: + type: string + updated_at: + type: string + format: date-time + required: + - repo_installation_id + - repo_id + - repo_full_name + - root_directory + - production_branch + - updated_at + ProjectHealthSummary: + type: object + properties: + status: + $ref: '#/components/schemas/ProjectHealthStatus' + required: + - status ProjectConfig: type: object additionalProperties: false diff --git a/src/volcano_sdk/_generated/models/__init__.py b/src/volcano_sdk/_generated/models/__init__.py index 300dc022..2f23373a 100644 --- a/src/volcano_sdk/_generated/models/__init__.py +++ b/src/volcano_sdk/_generated/models/__init__.py @@ -101,6 +101,7 @@ from .call_o_auth_provider_api_body_method import CallOAuthProviderAPIBodyMethod from .call_o_auth_provider_api_provider import CallOAuthProviderAPIProvider from .call_o_auth_provider_api_response_200 import CallOAuthProviderAPIResponse200 +from .call_o_auth_provider_api_response_200_provider import CallOAuthProviderAPIResponse200Provider from .complete_upload_session_response import CompleteUploadSessionResponse from .configure_auth_methods_body import ConfigureAuthMethodsBody from .configure_auth_methods_body_oauth_providers_item import ConfigureAuthMethodsBodyOauthProvidersItem @@ -601,6 +602,7 @@ "CallOAuthProviderAPIBodyMethod", "CallOAuthProviderAPIProvider", "CallOAuthProviderAPIResponse200", + "CallOAuthProviderAPIResponse200Provider", "CompleteUploadSessionResponse", "ConfigureAuthMethodsBody", "ConfigureAuthMethodsBodyOauthProvidersItem", diff --git a/src/volcano_sdk/_generated/models/call_o_auth_provider_api_response_200_provider.py b/src/volcano_sdk/_generated/models/call_o_auth_provider_api_response_200_provider.py new file mode 100644 index 00000000..1a657f40 --- /dev/null +++ b/src/volcano_sdk/_generated/models/call_o_auth_provider_api_response_200_provider.py @@ -0,0 +1,10 @@ +from typing import Literal + +CallOAuthProviderAPIResponse200Provider = Literal['apple', 'github', 'google', 'microsoft'] + +CALL_O_AUTH_PROVIDER_API_RESPONSE_200_PROVIDER_VALUES: set[CallOAuthProviderAPIResponse200Provider] = { 'apple', 'github', 'google', 'microsoft', } + +def check_call_o_auth_provider_api_response_200_provider(value: str) -> CallOAuthProviderAPIResponse200Provider: + if value in CALL_O_AUTH_PROVIDER_API_RESPONSE_200_PROVIDER_VALUES: + return value + raise TypeError(f"Unexpected value {value!r}. Expected one of {CALL_O_AUTH_PROVIDER_API_RESPONSE_200_PROVIDER_VALUES!r}") diff --git a/src/volcano_sdk/auth.py b/src/volcano_sdk/auth.py index da9a9d3d..95ff8a81 100644 --- a/src/volcano_sdk/auth.py +++ b/src/volcano_sdk/auth.py @@ -440,7 +440,7 @@ def _oauth_provider_token_status_from_payload( def _oauth_api_data_from_payload(payload: object) -> JSONValue: if not isinstance(payload, CallOAuthProviderAPIResponse200): raise VolcanoError(_INVALID_OAUTH_API_RESPONSE) - return _freeze_json(cast("JSONValue", payload.additional_properties)) + return _freeze_json(cast("JSONValue", payload.data)) class AuthContext(Protocol): diff --git a/tests/unit/test_state.py b/tests/unit/test_state.py index 9135bc5a..5c4ce367 100644 --- a/tests/unit/test_state.py +++ b/tests/unit/test_state.py @@ -316,7 +316,14 @@ def _configure_oauth(self) -> None: self.on_refresh_oauth_provider_token: Callable[[], None] | None = None self.call_oauth_api_response = Response( 200, - CallOAuthProviderAPIResponse200.from_dict({"name": "volcano"}), + CallOAuthProviderAPIResponse200.from_dict( + { + "provider": "github", + "endpoint": "/user/repos", + "status_code": 200, + "data": [{"name": "volcano"}], + } + ), ) self.call_oauth_api_calls: list[dict[str, Any]] = [] self.on_call_oauth_api: Callable[[], None] | None = None @@ -2293,7 +2300,7 @@ def test_call_oauth_api_returns_immutable_provider_data() -> None: body={"visibility": "private"}, ) - assert result == {"name": "volcano"} + assert result == ({"name": "volcano"},) assert client.auth.get_session() is established assert transport.call_oauth_api_calls == [ { @@ -2304,9 +2311,9 @@ def test_call_oauth_api_returns_immutable_provider_data() -> None: "body": {"visibility": "private"}, } ] - provider_data = cast("dict[str, object]", result) + repos = cast("tuple[dict[str, object], ...]", result) with pytest.raises(TypeError): - provider_data["name"] = "changed" + repos[0]["name"] = "changed" def test_call_oauth_api_rejects_an_unknown_provider() -> None: