From 7dbb9ba18814a4cb8dcbb8f81b34cf5e484e780f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:46:36 +0000 Subject: [PATCH 01/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index e42a5ca..cf214ce 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-3935e467f9c15925790aada293124db82bb5d6840eeac52d81fbac6a9b0fd154.yml -openapi_spec_hash: b417d7f10ea430216e9b70e4468a3212 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-bc2d9ee7f9a0aea1a4305b5487eac32742d6aea1e515616a8a2eda759cbb5b8f.yml +openapi_spec_hash: 1832681b749df9e2a56e2ca728572575 config_hash: d3267594264bfb76d2ee7e881d5f8a5a From 03bd1858ec2aefbd4c20a71c206135c441afa99c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:45:23 +0000 Subject: [PATCH 02/27] feat: Define SCIMConfiguration database schema --- .stats.yml | 4 ++-- src/gitpod/types/shared/resource_type.py | 1 + src/gitpod/types/shared_params/resource_type.py | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index cf214ce..df7a521 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-bc2d9ee7f9a0aea1a4305b5487eac32742d6aea1e515616a8a2eda759cbb5b8f.yml -openapi_spec_hash: 1832681b749df9e2a56e2ca728572575 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-f35d75f04023f8de20cc787ab5202c4853a79ea14fa8e639f598e034b312207f.yml +openapi_spec_hash: 0b2bf8b46a5bb29402fb9ae08ce6314f config_hash: d3267594264bfb76d2ee7e881d5f8a5a diff --git a/src/gitpod/types/shared/resource_type.py b/src/gitpod/types/shared/resource_type.py index 1a18f4e..7531c05 100644 --- a/src/gitpod/types/shared/resource_type.py +++ b/src/gitpod/types/shared/resource_type.py @@ -48,4 +48,5 @@ "RESOURCE_TYPE_ROLE_ASSIGNMENT_CHANGED", "RESOURCE_TYPE_GROUP_MEMBERSHIP_CHANGED", "RESOURCE_TYPE_WEBHOOK", + "RESOURCE_TYPE_SCIM_CONFIGURATION", ] diff --git a/src/gitpod/types/shared_params/resource_type.py b/src/gitpod/types/shared_params/resource_type.py index 793bab2..34d2f8c 100644 --- a/src/gitpod/types/shared_params/resource_type.py +++ b/src/gitpod/types/shared_params/resource_type.py @@ -50,4 +50,5 @@ "RESOURCE_TYPE_ROLE_ASSIGNMENT_CHANGED", "RESOURCE_TYPE_GROUP_MEMBERSHIP_CHANGED", "RESOURCE_TYPE_WEBHOOK", + "RESOURCE_TYPE_SCIM_CONFIGURATION", ] From 2847a10e6cbb09be83b012e8a6fcabd32f49e019 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:15:56 +0000 Subject: [PATCH 03/27] feat(api): improve SearchRepositories pagination with next_page and total_count --- .stats.yml | 4 ++-- .../runner_search_repositories_response.py | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.stats.yml b/.stats.yml index df7a521..7ce5377 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-f35d75f04023f8de20cc787ab5202c4853a79ea14fa8e639f598e034b312207f.yml -openapi_spec_hash: 0b2bf8b46a5bb29402fb9ae08ce6314f +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-b40906da1a429be933f59cd9c3b2f7197aef9b302033558e7cbb65579357240a.yml +openapi_spec_hash: bc0aec8cac7bfb2a69792bb43cfe50d2 config_hash: d3267594264bfb76d2ee7e881d5f8a5a diff --git a/src/gitpod/types/runner_search_repositories_response.py b/src/gitpod/types/runner_search_repositories_response.py index 65597de..e4f7350 100644 --- a/src/gitpod/types/runner_search_repositories_response.py +++ b/src/gitpod/types/runner_search_repositories_response.py @@ -10,7 +10,12 @@ class Pagination(BaseModel): - """Pagination information for the response""" + """ + Pagination information for the response. + Token format: "NEXT_PAGE/TOTAL_PAGES/TOTAL_COUNT" (e.g., "2/40/1000"). + Use -1 for unknown values (e.g., "2/-1/-1" when totals unavailable). + Empty token means no more pages. + """ next_token: Optional[str] = FieldInfo(alias="nextToken", default=None) """Token passed for retrieving the next set of results. @@ -29,10 +34,18 @@ class Repository(BaseModel): class RunnerSearchRepositoriesResponse(BaseModel): last_page: Optional[int] = FieldInfo(alias="lastPage", default=None) - """Last page in the responses""" + """Deprecated: Use pagination token instead. + + Total pages can be extracted from token. + """ pagination: Optional[Pagination] = None - """Pagination information for the response""" + """ + Pagination information for the response. Token format: + "NEXT_PAGE/TOTAL_PAGES/TOTAL_COUNT" (e.g., "2/40/1000"). Use -1 for unknown + values (e.g., "2/-1/-1" when totals unavailable). Empty token means no more + pages. + """ repositories: Optional[List[Repository]] = None """List of repositories matching the search criteria""" From 1bc205897a717cae24f866eee3db2f91148b9ee7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:28:32 +0000 Subject: [PATCH 04/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7ce5377..d535149 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-b40906da1a429be933f59cd9c3b2f7197aef9b302033558e7cbb65579357240a.yml -openapi_spec_hash: bc0aec8cac7bfb2a69792bb43cfe50d2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-46e0124fe8e07933a6ea784e81b4d02086696c246f519aaa34b087f46410c818.yml +openapi_spec_hash: fe98c15ea1e320c05593cd57457e4af5 config_hash: d3267594264bfb76d2ee7e881d5f8a5a From be5a8235224ff1ecf25464e716191fbf3c7c7fb1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:36:02 +0000 Subject: [PATCH 05/27] feat(client): add support for binary request streaming --- src/gitpod/_base_client.py | 145 +++++++++++++++++++++++++--- src/gitpod/_models.py | 17 +++- src/gitpod/_types.py | 9 ++ tests/test_client.py | 187 ++++++++++++++++++++++++++++++++++++- 4 files changed, 344 insertions(+), 14 deletions(-) diff --git a/src/gitpod/_base_client.py b/src/gitpod/_base_client.py index 1613979..e6ccedf 100644 --- a/src/gitpod/_base_client.py +++ b/src/gitpod/_base_client.py @@ -9,6 +9,7 @@ import inspect import logging import platform +import warnings import email.utils from types import TracebackType from random import random @@ -51,9 +52,11 @@ ResponseT, AnyMapping, PostParser, + BinaryTypes, RequestFiles, HttpxSendArgs, RequestOptions, + AsyncBinaryTypes, HttpxRequestFiles, ModelBuilderProtocol, not_given, @@ -477,8 +480,19 @@ def _build_request( retries_taken: int = 0, ) -> httpx.Request: if log.isEnabledFor(logging.DEBUG): - log.debug("Request options: %s", model_dump(options, exclude_unset=True)) - + log.debug( + "Request options: %s", + model_dump( + options, + exclude_unset=True, + # Pydantic v1 can't dump every type we support in content, so we exclude it for now. + exclude={ + "content", + } + if PYDANTIC_V1 + else {}, + ), + ) kwargs: dict[str, Any] = {} json_data = options.json_data @@ -532,7 +546,13 @@ def _build_request( is_body_allowed = options.method.lower() != "get" if is_body_allowed: - if isinstance(json_data, bytes): + if options.content is not None and json_data is not None: + raise TypeError("Passing both `content` and `json_data` is not supported") + if options.content is not None and files is not None: + raise TypeError("Passing both `content` and `files` is not supported") + if options.content is not None: + kwargs["content"] = options.content + elif isinstance(json_data, bytes): kwargs["content"] = json_data else: kwargs["json"] = json_data if is_given(json_data) else None @@ -1194,6 +1214,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[False] = False, @@ -1206,6 +1227,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[True], @@ -1219,6 +1241,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool, @@ -1231,13 +1254,25 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, files=to_httpx_files(files), **options + method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) @@ -1247,11 +1282,23 @@ def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) @@ -1261,11 +1308,23 @@ def put( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, files=to_httpx_files(files), **options + method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) @@ -1275,9 +1334,19 @@ def delete( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return self.request(cast_to, opts) def get_api_list( @@ -1717,6 +1786,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[False] = False, @@ -1729,6 +1799,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[True], @@ -1742,6 +1813,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool, @@ -1754,13 +1826,25 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) @@ -1770,11 +1854,28 @@ async def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="patch", + url=path, + json_data=body, + content=content, + files=await async_to_httpx_files(files), + **options, ) return await self.request(cast_to, opts) @@ -1784,11 +1885,23 @@ async def put( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts) @@ -1798,9 +1911,19 @@ async def delete( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return await self.request(cast_to, opts) def get_api_list( diff --git a/src/gitpod/_models.py b/src/gitpod/_models.py index ca9500b..29070e0 100644 --- a/src/gitpod/_models.py +++ b/src/gitpod/_models.py @@ -3,7 +3,20 @@ import os import inspect import weakref -from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast +from typing import ( + IO, + TYPE_CHECKING, + Any, + Type, + Union, + Generic, + TypeVar, + Callable, + Iterable, + Optional, + AsyncIterable, + cast, +) from datetime import date, datetime from typing_extensions import ( List, @@ -787,6 +800,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): timeout: float | Timeout | None files: HttpxRequestFiles | None idempotency_key: str + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] json_data: Body extra_json: AnyMapping follow_redirects: bool @@ -805,6 +819,7 @@ class FinalRequestOptions(pydantic.BaseModel): post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() follow_redirects: Union[bool, None] = None + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None # It should be noted that we cannot use `json` here as that would override # a BaseModel method in an incompatible fashion. json_data: Union[Body, None] = None diff --git a/src/gitpod/_types.py b/src/gitpod/_types.py index ab7cbe2..dbebac0 100644 --- a/src/gitpod/_types.py +++ b/src/gitpod/_types.py @@ -13,9 +13,11 @@ Mapping, TypeVar, Callable, + Iterable, Iterator, Optional, Sequence, + AsyncIterable, ) from typing_extensions import ( Set, @@ -56,6 +58,13 @@ else: Base64FileInput = Union[IO[bytes], PathLike] FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. + + +# Used for sending raw binary data / streaming data in request bodies +# e.g. for file uploads without multipart encoding +BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]] +AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]] + FileTypes = Union[ # file (or bytes) FileContent, diff --git a/tests/test_client.py b/tests/test_client.py index f948b8a..81db6c8 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -8,10 +8,11 @@ import json import asyncio import inspect +import dataclasses import tracemalloc -from typing import Any, Union, cast +from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast from unittest import mock -from typing_extensions import Literal +from typing_extensions import Literal, AsyncIterator, override import httpx import pytest @@ -36,6 +37,7 @@ from .utils import update_env +T = TypeVar("T") base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") bearer_token = "My Bearer Token" @@ -50,6 +52,57 @@ def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: return 0.1 +def mirror_request_content(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, content=request.content) + + +# note: we can't use the httpx.MockTransport class as it consumes the request +# body itself, which means we can't test that the body is read lazily +class MockTransport(httpx.BaseTransport, httpx.AsyncBaseTransport): + def __init__( + self, + handler: Callable[[httpx.Request], httpx.Response] + | Callable[[httpx.Request], Coroutine[Any, Any, httpx.Response]], + ) -> None: + self.handler = handler + + @override + def handle_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert not inspect.iscoroutinefunction(self.handler), "handler must not be a coroutine function" + assert inspect.isfunction(self.handler), "handler must be a function" + return self.handler(request) + + @override + async def handle_async_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert inspect.iscoroutinefunction(self.handler), "handler must be a coroutine function" + return await self.handler(request) + + +@dataclasses.dataclass +class Counter: + value: int = 0 + + +def _make_sync_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> Iterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + +async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> AsyncIterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + def _get_open_connections(client: Gitpod | AsyncGitpod) -> int: transport = client._client._transport assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) @@ -511,6 +564,70 @@ def test_multipart_repeating_array(self, client: Gitpod) -> None: b"", ] + @pytest.mark.respx(base_url=base_url) + def test_binary_content_upload(self, respx_mock: MockRouter, client: Gitpod) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + def test_binary_content_upload_with_iterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_sync_iterator([file_content], counter=counter) + + def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=request.read()) + + with Gitpod( + base_url=base_url, + bearer_token=bearer_token, + _strict_response_validation=True, + http_client=httpx.Client(transport=MockTransport(handler=mock_handler)), + ) as client: + response = client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: Gitpod) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + @pytest.mark.respx(base_url=base_url) def test_basic_union_response(self, respx_mock: MockRouter, client: Gitpod) -> None: class Model1(BaseModel): @@ -1364,6 +1481,72 @@ def test_multipart_repeating_array(self, async_client: AsyncGitpod) -> None: b"", ] + @pytest.mark.respx(base_url=base_url) + async def test_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncGitpod) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = await async_client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + async def test_binary_content_upload_with_asynciterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_async_iterator([file_content], counter=counter) + + async def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=await request.aread()) + + async with AsyncGitpod( + base_url=base_url, + bearer_token=bearer_token, + _strict_response_validation=True, + http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)), + ) as client: + response = await client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + async def test_binary_content_upload_with_body_is_deprecated( + self, respx_mock: MockRouter, async_client: AsyncGitpod + ) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = await async_client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + @pytest.mark.respx(base_url=base_url) async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncGitpod) -> None: class Model1(BaseModel): From a55115ba054078dcb689222cc150b2b1f56077bf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 05:26:53 +0000 Subject: [PATCH 06/27] feat: move agent mode from Spec to Status, add AgentModeChange signals --- .stats.yml | 4 ++-- src/gitpod/types/agent_execution.py | 9 ++++++--- src/gitpod/types/agent_mode.py | 4 +++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.stats.yml b/.stats.yml index d535149..a59a947 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-46e0124fe8e07933a6ea784e81b4d02086696c246f519aaa34b087f46410c818.yml -openapi_spec_hash: fe98c15ea1e320c05593cd57457e4af5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-cad9422301ea573f7fdbcf5047e421a0a6e963468a9d8f464e47ed1800a8fc75.yml +openapi_spec_hash: b2e5072d4659a5c4fb5d2e8916388af9 config_hash: d3267594264bfb76d2ee7e881d5f8a5a diff --git a/src/gitpod/types/agent_execution.py b/src/gitpod/types/agent_execution.py index 384da90..ad11938 100644 --- a/src/gitpod/types/agent_execution.py +++ b/src/gitpod/types/agent_execution.py @@ -259,9 +259,6 @@ class Spec(BaseModel): limits: Optional[SpecLimits] = None - mode: Optional[AgentMode] = None - """mode is the operational mode for this agent execution""" - session: Optional[str] = None spec_version: Optional[str] = FieldInfo(alias="specVersion", default=None) @@ -355,6 +352,12 @@ class Status(BaseModel): judgement: Optional[str] = None """judgement is the judgement of the agent run produced by the judgement prompt.""" + mode: Optional[AgentMode] = None + """ + mode is the current operational mode of the agent execution. This is set by the + agent when entering different modes (e.g., Ralph mode via /ona:ralph command). + """ + outputs: Optional[Dict[str, StatusOutputs]] = None """ outputs is a map of key-value pairs that can be set by the agent during diff --git a/src/gitpod/types/agent_mode.py b/src/gitpod/types/agent_mode.py index de414c7..d7bd23b 100644 --- a/src/gitpod/types/agent_mode.py +++ b/src/gitpod/types/agent_mode.py @@ -4,4 +4,6 @@ __all__ = ["AgentMode"] -AgentMode: TypeAlias = Literal["AGENT_MODE_UNSPECIFIED", "AGENT_MODE_EXECUTION", "AGENT_MODE_PLANNING"] +AgentMode: TypeAlias = Literal[ + "AGENT_MODE_UNSPECIFIED", "AGENT_MODE_EXECUTION", "AGENT_MODE_PLANNING", "AGENT_MODE_RALPH" +] From e0023da5a30344c2fc87ebce55e26101c4ad40b5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:02:52 +0000 Subject: [PATCH 07/27] feat(api): add draft and state fields to PullRequest proto --- .stats.yml | 6 +++--- api.md | 1 + src/gitpod/types/__init__.py | 1 + src/gitpod/types/agent_code_context.py | 7 +++++++ src/gitpod/types/agent_code_context_param.py | 7 +++++++ src/gitpod/types/runner_parse_context_url_response.py | 7 +++++++ src/gitpod/types/shared/__init__.py | 1 + src/gitpod/types/shared/state.py | 7 +++++++ src/gitpod/types/shared_params/__init__.py | 1 + src/gitpod/types/shared_params/state.py | 9 +++++++++ tests/api_resources/test_agents.py | 4 ++++ 11 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/gitpod/types/shared/state.py create mode 100644 src/gitpod/types/shared_params/state.py diff --git a/.stats.yml b/.stats.yml index a59a947..9004557 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-cad9422301ea573f7fdbcf5047e421a0a6e963468a9d8f464e47ed1800a8fc75.yml -openapi_spec_hash: b2e5072d4659a5c4fb5d2e8916388af9 -config_hash: d3267594264bfb76d2ee7e881d5f8a5a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-47577991d03c0dab9d0f219bd3f6cf8cb3ea386a919a10c949318091aca5b10f.yml +openapi_spec_hash: ad236fd154210c60d9aca150a3fd51bc +config_hash: f36d04c8359fe8baec226396a18b309e diff --git a/api.md b/api.md index 29f93a4..36e2403 100644 --- a/api.md +++ b/api.md @@ -15,6 +15,7 @@ from gitpod.types import ( ResourceType, RunsOn, SecretRef, + State, Subject, Task, TaskExecution, diff --git a/src/gitpod/types/__init__.py b/src/gitpod/types/__init__.py index bd85a24..954f9df 100644 --- a/src/gitpod/types/__init__.py +++ b/src/gitpod/types/__init__.py @@ -10,6 +10,7 @@ from .secret import Secret as Secret from .shared import ( Task as Task, + State as State, RunsOn as RunsOn, Gateway as Gateway, Subject as Subject, diff --git a/src/gitpod/types/agent_code_context.py b/src/gitpod/types/agent_code_context.py index 05c1832..ece7309 100644 --- a/src/gitpod/types/agent_code_context.py +++ b/src/gitpod/types/agent_code_context.py @@ -5,6 +5,7 @@ from pydantic import Field as FieldInfo from .._models import BaseModel +from .shared.state import State __all__ = ["AgentCodeContext", "ContextURL", "PullRequest", "PullRequestRepository"] @@ -40,12 +41,18 @@ class PullRequest(BaseModel): author: Optional[str] = None """Author name as provided by the SCM system""" + draft: Optional[bool] = None + """Whether this is a draft pull request""" + from_branch: Optional[str] = FieldInfo(alias="fromBranch", default=None) """Source branch name (the branch being merged from)""" repository: Optional[PullRequestRepository] = None """Repository information""" + state: Optional[State] = None + """Current state of the pull request""" + title: Optional[str] = None """Pull request title""" diff --git a/src/gitpod/types/agent_code_context_param.py b/src/gitpod/types/agent_code_context_param.py index 8080145..821f296 100644 --- a/src/gitpod/types/agent_code_context_param.py +++ b/src/gitpod/types/agent_code_context_param.py @@ -6,6 +6,7 @@ from typing_extensions import Annotated, TypedDict from .._utils import PropertyInfo +from .shared.state import State __all__ = ["AgentCodeContextParam", "ContextURL", "PullRequest", "PullRequestRepository"] @@ -41,12 +42,18 @@ class PullRequest(TypedDict, total=False): author: str """Author name as provided by the SCM system""" + draft: bool + """Whether this is a draft pull request""" + from_branch: Annotated[str, PropertyInfo(alias="fromBranch")] """Source branch name (the branch being merged from)""" repository: PullRequestRepository """Repository information""" + state: State + """Current state of the pull request""" + title: str """Pull request title""" diff --git a/src/gitpod/types/runner_parse_context_url_response.py b/src/gitpod/types/runner_parse_context_url_response.py index c712e40..c18fd7d 100644 --- a/src/gitpod/types/runner_parse_context_url_response.py +++ b/src/gitpod/types/runner_parse_context_url_response.py @@ -5,6 +5,7 @@ from pydantic import Field as FieldInfo from .._models import BaseModel +from .shared.state import State __all__ = ["RunnerParseContextURLResponse", "Git", "Issue", "Pr", "PullRequest", "PullRequestRepository"] @@ -71,12 +72,18 @@ class PullRequest(BaseModel): author: Optional[str] = None """Author name as provided by the SCM system""" + draft: Optional[bool] = None + """Whether this is a draft pull request""" + from_branch: Optional[str] = FieldInfo(alias="fromBranch", default=None) """Source branch name (the branch being merged from)""" repository: Optional[PullRequestRepository] = None """Repository information""" + state: Optional[State] = None + """Current state of the pull request""" + title: Optional[str] = None """Pull request title""" diff --git a/src/gitpod/types/shared/__init__.py b/src/gitpod/types/shared/__init__.py index 50c1315..5bfea28 100644 --- a/src/gitpod/types/shared/__init__.py +++ b/src/gitpod/types/shared/__init__.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .task import Task as Task +from .state import State as State from .gateway import Gateway as Gateway from .runs_on import RunsOn as RunsOn from .subject import Subject as Subject diff --git a/src/gitpod/types/shared/state.py b/src/gitpod/types/shared/state.py new file mode 100644 index 0000000..92aa0de --- /dev/null +++ b/src/gitpod/types/shared/state.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["State"] + +State: TypeAlias = Literal["STATE_UNSPECIFIED", "STATE_OPEN", "STATE_CLOSED", "STATE_MERGED"] diff --git a/src/gitpod/types/shared_params/__init__.py b/src/gitpod/types/shared_params/__init__.py index 4673c76..e91ec61 100644 --- a/src/gitpod/types/shared_params/__init__.py +++ b/src/gitpod/types/shared_params/__init__.py @@ -1,5 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +from .state import State as State from .runs_on import RunsOn as RunsOn from .subject import Subject as Subject from .principal import Principal as Principal diff --git a/src/gitpod/types/shared_params/state.py b/src/gitpod/types/shared_params/state.py new file mode 100644 index 0000000..1d81f94 --- /dev/null +++ b/src/gitpod/types/shared_params/state.py @@ -0,0 +1,9 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypeAlias + +__all__ = ["State"] + +State: TypeAlias = Literal["STATE_UNSPECIFIED", "STATE_OPEN", "STATE_CLOSED", "STATE_MERGED"] diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py index b92173b..f4f2aad 100644 --- a/tests/api_resources/test_agents.py +++ b/tests/api_resources/test_agents.py @@ -410,6 +410,7 @@ def test_method_start_execution_with_all_params(self, client: Gitpod) -> None: "pull_request": { "id": "id", "author": "author", + "draft": True, "from_branch": "fromBranch", "repository": { "clone_url": "cloneUrl", @@ -417,6 +418,7 @@ def test_method_start_execution_with_all_params(self, client: Gitpod) -> None: "name": "name", "owner": "owner", }, + "state": "STATE_UNSPECIFIED", "title": "title", "to_branch": "toBranch", "url": "url", @@ -921,6 +923,7 @@ async def test_method_start_execution_with_all_params(self, async_client: AsyncG "pull_request": { "id": "id", "author": "author", + "draft": True, "from_branch": "fromBranch", "repository": { "clone_url": "cloneUrl", @@ -928,6 +931,7 @@ async def test_method_start_execution_with_all_params(self, async_client: AsyncG "name": "name", "owner": "owner", }, + "state": "STATE_UNSPECIFIED", "title": "title", "to_branch": "toBranch", "url": "url", From 838e74c4da4b57590a6dd0af19bdd20faf7d2805 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 18:04:27 +0000 Subject: [PATCH 08/27] feat: [api] sorting for `ListMembers` --- .stats.yml | 4 +-- .../resources/organizations/organizations.py | 14 ++++++++++ .../types/organization_list_members_params.py | 27 +++++++++++++++++-- tests/api_resources/test_organizations.py | 8 ++++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9004557..3117cef 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-47577991d03c0dab9d0f219bd3f6cf8cb3ea386a919a10c949318091aca5b10f.yml -openapi_spec_hash: ad236fd154210c60d9aca150a3fd51bc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-3a8fb9d6b9645a483a08206e944cc388325e210f0bd54daa9e15ee561a37fabc.yml +openapi_spec_hash: fe42cbf3b012e4aebf56f64a675c3dd3 config_hash: f36d04c8359fe8baec226396a18b309e diff --git a/src/gitpod/resources/organizations/organizations.py b/src/gitpod/resources/organizations/organizations.py index c1ec57e..c9de4ae 100644 --- a/src/gitpod/resources/organizations/organizations.py +++ b/src/gitpod/resources/organizations/organizations.py @@ -507,6 +507,7 @@ def list_members( page_size: int | Omit = omit, filter: organization_list_members_params.Filter | Omit = omit, pagination: organization_list_members_params.Pagination | Omit = omit, + sort: organization_list_members_params.Sort | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -551,6 +552,11 @@ def list_members( pagination: pagination contains the pagination options for listing members + sort: sort specifies the order of results. When unspecified, the authenticated user is + returned first, followed by other members sorted by name ascending. When an + explicit sort is specified, results are sorted purely by the requested field + without any special handling for the authenticated user. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -567,6 +573,7 @@ def list_members( "organization_id": organization_id, "filter": filter, "pagination": pagination, + "sort": sort, }, organization_list_members_params.OrganizationListMembersParams, ), @@ -1086,6 +1093,7 @@ def list_members( page_size: int | Omit = omit, filter: organization_list_members_params.Filter | Omit = omit, pagination: organization_list_members_params.Pagination | Omit = omit, + sort: organization_list_members_params.Sort | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1130,6 +1138,11 @@ def list_members( pagination: pagination contains the pagination options for listing members + sort: sort specifies the order of results. When unspecified, the authenticated user is + returned first, followed by other members sorted by name ascending. When an + explicit sort is specified, results are sorted purely by the requested field + without any special handling for the authenticated user. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1146,6 +1159,7 @@ def list_members( "organization_id": organization_id, "filter": filter, "pagination": pagination, + "sort": sort, }, organization_list_members_params.OrganizationListMembersParams, ), diff --git a/src/gitpod/types/organization_list_members_params.py b/src/gitpod/types/organization_list_members_params.py index 06c529e..16d5cba 100644 --- a/src/gitpod/types/organization_list_members_params.py +++ b/src/gitpod/types/organization_list_members_params.py @@ -2,11 +2,11 @@ from __future__ import annotations -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict from .._utils import PropertyInfo -__all__ = ["OrganizationListMembersParams", "Filter", "Pagination"] +__all__ = ["OrganizationListMembersParams", "Filter", "Pagination", "Sort"] class OrganizationListMembersParams(TypedDict, total=False): @@ -22,6 +22,15 @@ class OrganizationListMembersParams(TypedDict, total=False): pagination: Pagination """pagination contains the pagination options for listing members""" + sort: Sort + """sort specifies the order of results. + + When unspecified, the authenticated user is returned first, followed by other + members sorted by name ascending. When an explicit sort is specified, results + are sorted purely by the requested field without any special handling for the + authenticated user. + """ + class Filter(TypedDict, total=False): search: str @@ -42,3 +51,17 @@ class Pagination(TypedDict, total=False): Maximum 100. """ + + +class Sort(TypedDict, total=False): + """sort specifies the order of results. + + When unspecified, the authenticated user is + returned first, followed by other members sorted by name ascending. When an explicit + sort is specified, results are sorted purely by the requested field without any + special handling for the authenticated user. + """ + + field: Literal["SORT_FIELD_UNSPECIFIED", "SORT_FIELD_NAME", "SORT_FIELD_DATE_JOINED"] + + order: Literal["SORT_ORDER_UNSPECIFIED", "SORT_ORDER_ASC", "SORT_ORDER_DESC"] diff --git a/tests/api_resources/test_organizations.py b/tests/api_resources/test_organizations.py index 8cda4e7..644ccbb 100644 --- a/tests/api_resources/test_organizations.py +++ b/tests/api_resources/test_organizations.py @@ -271,6 +271,10 @@ def test_method_list_members_with_all_params(self, client: Gitpod) -> None: "token": "token", "page_size": 20, }, + sort={ + "field": "SORT_FIELD_UNSPECIFIED", + "order": "SORT_ORDER_UNSPECIFIED", + }, ) assert_matches_type(SyncMembersPage[OrganizationMember], organization, path=["response"]) @@ -600,6 +604,10 @@ async def test_method_list_members_with_all_params(self, async_client: AsyncGitp "token": "token", "page_size": 20, }, + sort={ + "field": "SORT_FIELD_UNSPECIFIED", + "order": "SORT_ORDER_UNSPECIFIED", + }, ) assert_matches_type(AsyncMembersPage[OrganizationMember], organization, path=["response"]) From 34fb372aef655ae57fc99d5b37e152c75d831af5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 19:42:10 +0000 Subject: [PATCH 09/27] feat: [backend] Introduce role and member status filtering for `ListMembers` --- .stats.yml | 4 ++-- src/gitpod/types/organization_list_members_params.py | 9 +++++++++ src/gitpod/types/shared_params/__init__.py | 1 + src/gitpod/types/shared_params/user_status.py | 11 +++++++++++ tests/api_resources/test_organizations.py | 12 ++++++++++-- 5 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 src/gitpod/types/shared_params/user_status.py diff --git a/.stats.yml b/.stats.yml index 3117cef..f36c8c0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-3a8fb9d6b9645a483a08206e944cc388325e210f0bd54daa9e15ee561a37fabc.yml -openapi_spec_hash: fe42cbf3b012e4aebf56f64a675c3dd3 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-13ed0159480c5c02ed02394764e7c528c1110cdc42d2a0ff5fac228f48403a08.yml +openapi_spec_hash: e1b4c20a8a5faee0f2dd22b449e7a106 config_hash: f36d04c8359fe8baec226396a18b309e diff --git a/src/gitpod/types/organization_list_members_params.py b/src/gitpod/types/organization_list_members_params.py index 16d5cba..2e5f87e 100644 --- a/src/gitpod/types/organization_list_members_params.py +++ b/src/gitpod/types/organization_list_members_params.py @@ -2,9 +2,12 @@ from __future__ import annotations +from typing import List from typing_extensions import Literal, Required, Annotated, TypedDict from .._utils import PropertyInfo +from .shared.user_status import UserStatus +from .shared.organization_role import OrganizationRole __all__ = ["OrganizationListMembersParams", "Filter", "Pagination", "Sort"] @@ -33,9 +36,15 @@ class OrganizationListMembersParams(TypedDict, total=False): class Filter(TypedDict, total=False): + roles: List[OrganizationRole] + """roles filters members by their organization role""" + search: str """search performs case-insensitive search across member name and email""" + statuses: List[UserStatus] + """status filters members by their user status""" + class Pagination(TypedDict, total=False): """pagination contains the pagination options for listing members""" diff --git a/src/gitpod/types/shared_params/__init__.py b/src/gitpod/types/shared_params/__init__.py index e91ec61..093bf76 100644 --- a/src/gitpod/types/shared_params/__init__.py +++ b/src/gitpod/types/shared_params/__init__.py @@ -7,6 +7,7 @@ from .task_spec import TaskSpec as TaskSpec from .secret_ref import SecretRef as SecretRef from .field_value import FieldValue as FieldValue +from .user_status import UserStatus as UserStatus from .resource_type import ResourceType as ResourceType from .task_metadata import TaskMetadata as TaskMetadata from .environment_class import EnvironmentClass as EnvironmentClass diff --git a/src/gitpod/types/shared_params/user_status.py b/src/gitpod/types/shared_params/user_status.py new file mode 100644 index 0000000..fd8ac19 --- /dev/null +++ b/src/gitpod/types/shared_params/user_status.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypeAlias + +__all__ = ["UserStatus"] + +UserStatus: TypeAlias = Literal[ + "USER_STATUS_UNSPECIFIED", "USER_STATUS_ACTIVE", "USER_STATUS_SUSPENDED", "USER_STATUS_LEFT" +] diff --git a/tests/api_resources/test_organizations.py b/tests/api_resources/test_organizations.py index 644ccbb..b404eb7 100644 --- a/tests/api_resources/test_organizations.py +++ b/tests/api_resources/test_organizations.py @@ -266,7 +266,11 @@ def test_method_list_members_with_all_params(self, client: Gitpod) -> None: organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", token="token", page_size=0, - filter={"search": "search"}, + filter={ + "roles": ["ORGANIZATION_ROLE_UNSPECIFIED"], + "search": "search", + "statuses": ["USER_STATUS_UNSPECIFIED"], + }, pagination={ "token": "token", "page_size": 20, @@ -599,7 +603,11 @@ async def test_method_list_members_with_all_params(self, async_client: AsyncGitp organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", token="token", page_size=0, - filter={"search": "search"}, + filter={ + "roles": ["ORGANIZATION_ROLE_UNSPECIFIED"], + "search": "search", + "statuses": ["USER_STATUS_UNSPECIFIED"], + }, pagination={ "token": "token", "page_size": 20, From 89fd8fef7f9de200e4aecd563c965d4209427052 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 21:11:51 +0000 Subject: [PATCH 10/27] feat(dashboard): show tier badge in org selector --- .stats.yml | 6 +++--- api.md | 2 +- src/gitpod/types/__init__.py | 2 +- src/gitpod/types/account_membership.py | 8 ++++++-- src/gitpod/types/organization.py | 2 +- src/gitpod/types/shared/__init__.py | 1 + src/gitpod/types/{ => shared}/organization_tier.py | 0 7 files changed, 13 insertions(+), 8 deletions(-) rename src/gitpod/types/{ => shared}/organization_tier.py (100%) diff --git a/.stats.yml b/.stats.yml index f36c8c0..5770e67 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-13ed0159480c5c02ed02394764e7c528c1110cdc42d2a0ff5fac228f48403a08.yml -openapi_spec_hash: e1b4c20a8a5faee0f2dd22b449e7a106 -config_hash: f36d04c8359fe8baec226396a18b309e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-504fab16d01ad7f81914542fd0523f4dfefce87813891da391b97b1b0a70f563.yml +openapi_spec_hash: 84d9d652cac365c646e01111cee89afc +config_hash: acfe8cf5d6e4b26387e4fefa3bff8409 diff --git a/api.md b/api.md index 36e2403..9b1a07d 100644 --- a/api.md +++ b/api.md @@ -10,6 +10,7 @@ from gitpod.types import ( FieldValue, Gateway, OrganizationRole, + OrganizationTier, Principal, ProjectEnvironmentClass, ResourceType, @@ -338,7 +339,6 @@ from gitpod.types import ( InviteDomains, Organization, OrganizationMember, - OrganizationTier, OrganizationCreateResponse, OrganizationRetrieveResponse, OrganizationUpdateResponse, diff --git a/src/gitpod/types/__init__.py b/src/gitpod/types/__init__.py index 954f9df..842817b 100644 --- a/src/gitpod/types/__init__.py +++ b/src/gitpod/types/__init__.py @@ -25,6 +25,7 @@ TaskExecution as TaskExecution, EnvironmentClass as EnvironmentClass, OrganizationRole as OrganizationRole, + OrganizationTier as OrganizationTier, AutomationTrigger as AutomationTrigger, TaskExecutionSpec as TaskExecutionSpec, TaskExecutionPhase as TaskExecutionPhase, @@ -72,7 +73,6 @@ from .error_event_param import ErrorEventParam as ErrorEventParam from .event_list_params import EventListParams as EventListParams from .group_list_params import GroupListParams as GroupListParams -from .organization_tier import OrganizationTier as OrganizationTier from .prebuild_metadata import PrebuildMetadata as PrebuildMetadata from .runner_capability import RunnerCapability as RunnerCapability from .runner_spec_param import RunnerSpecParam as RunnerSpecParam diff --git a/src/gitpod/types/account_membership.py b/src/gitpod/types/account_membership.py index 10f2603..6f443a8 100644 --- a/src/gitpod/types/account_membership.py +++ b/src/gitpod/types/account_membership.py @@ -6,6 +6,7 @@ from .._models import BaseModel from .shared.organization_role import OrganizationRole +from .shared.organization_tier import OrganizationTier __all__ = ["AccountMembership"] @@ -25,6 +26,9 @@ class AccountMembership(BaseModel): organization_member_count: Optional[int] = FieldInfo(alias="organizationMemberCount", default=None) """ - organization_name is the member count of the organization the user is a member - of + organization_member_count is the member count of the organization the user is a + member of """ + + organization_tier: Optional[OrganizationTier] = FieldInfo(alias="organizationTier", default=None) + """organization_tier is the tier of the organization (Free, Core, Enterprise)""" diff --git a/src/gitpod/types/organization.py b/src/gitpod/types/organization.py index edf88f1..e833b09 100644 --- a/src/gitpod/types/organization.py +++ b/src/gitpod/types/organization.py @@ -7,7 +7,7 @@ from .._models import BaseModel from .invite_domains import InviteDomains -from .organization_tier import OrganizationTier +from .shared.organization_tier import OrganizationTier __all__ = ["Organization"] diff --git a/src/gitpod/types/shared/__init__.py b/src/gitpod/types/shared/__init__.py index 5bfea28..de9766f 100644 --- a/src/gitpod/types/shared/__init__.py +++ b/src/gitpod/types/shared/__init__.py @@ -16,6 +16,7 @@ from .task_execution import TaskExecution as TaskExecution from .environment_class import EnvironmentClass as EnvironmentClass from .organization_role import OrganizationRole as OrganizationRole +from .organization_tier import OrganizationTier as OrganizationTier from .automation_trigger import AutomationTrigger as AutomationTrigger from .task_execution_spec import TaskExecutionSpec as TaskExecutionSpec from .task_execution_phase import TaskExecutionPhase as TaskExecutionPhase diff --git a/src/gitpod/types/organization_tier.py b/src/gitpod/types/shared/organization_tier.py similarity index 100% rename from src/gitpod/types/organization_tier.py rename to src/gitpod/types/shared/organization_tier.py From 30e17c55b991286527f64c8857b04dd9b5a2ba7b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 11:17:51 +0000 Subject: [PATCH 11/27] feat(secrets): add ServiceAccountSecret entity with full support --- .stats.yml | 4 ++-- src/gitpod/types/secret_scope.py | 3 +++ src/gitpod/types/secret_scope_param.py | 3 +++ src/gitpod/types/shared/resource_type.py | 1 + src/gitpod/types/shared_params/resource_type.py | 1 + tests/api_resources/test_secrets.py | 4 ++++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5770e67..a1fa096 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-504fab16d01ad7f81914542fd0523f4dfefce87813891da391b97b1b0a70f563.yml -openapi_spec_hash: 84d9d652cac365c646e01111cee89afc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-8a0189eebf423961bd98075dab28db4377c3a939da1dcf237d1b3958724e6dcf.yml +openapi_spec_hash: 48004e7605cf720584e822ec11a2eb8b config_hash: acfe8cf5d6e4b26387e4fefa3bff8409 diff --git a/src/gitpod/types/secret_scope.py b/src/gitpod/types/secret_scope.py index f66b672..1d01cb3 100644 --- a/src/gitpod/types/secret_scope.py +++ b/src/gitpod/types/secret_scope.py @@ -16,5 +16,8 @@ class SecretScope(BaseModel): project_id: Optional[str] = FieldInfo(alias="projectId", default=None) """project_id is the Project ID this Secret belongs to""" + service_account_id: Optional[str] = FieldInfo(alias="serviceAccountId", default=None) + """service_account_id is the Service Account ID this Secret belongs to""" + user_id: Optional[str] = FieldInfo(alias="userId", default=None) """user_id is the User ID this Secret belongs to""" diff --git a/src/gitpod/types/secret_scope_param.py b/src/gitpod/types/secret_scope_param.py index d68e56b..b7cf4f4 100644 --- a/src/gitpod/types/secret_scope_param.py +++ b/src/gitpod/types/secret_scope_param.py @@ -16,5 +16,8 @@ class SecretScopeParam(TypedDict, total=False): project_id: Annotated[str, PropertyInfo(alias="projectId")] """project_id is the Project ID this Secret belongs to""" + service_account_id: Annotated[str, PropertyInfo(alias="serviceAccountId")] + """service_account_id is the Service Account ID this Secret belongs to""" + user_id: Annotated[str, PropertyInfo(alias="userId")] """user_id is the User ID this Secret belongs to""" diff --git a/src/gitpod/types/shared/resource_type.py b/src/gitpod/types/shared/resource_type.py index 7531c05..599108b 100644 --- a/src/gitpod/types/shared/resource_type.py +++ b/src/gitpod/types/shared/resource_type.py @@ -49,4 +49,5 @@ "RESOURCE_TYPE_GROUP_MEMBERSHIP_CHANGED", "RESOURCE_TYPE_WEBHOOK", "RESOURCE_TYPE_SCIM_CONFIGURATION", + "RESOURCE_TYPE_SERVICE_ACCOUNT_SECRET", ] diff --git a/src/gitpod/types/shared_params/resource_type.py b/src/gitpod/types/shared_params/resource_type.py index 34d2f8c..9d489e4 100644 --- a/src/gitpod/types/shared_params/resource_type.py +++ b/src/gitpod/types/shared_params/resource_type.py @@ -51,4 +51,5 @@ "RESOURCE_TYPE_GROUP_MEMBERSHIP_CHANGED", "RESOURCE_TYPE_WEBHOOK", "RESOURCE_TYPE_SCIM_CONFIGURATION", + "RESOURCE_TYPE_SERVICE_ACCOUNT_SECRET", ] diff --git a/tests/api_resources/test_secrets.py b/tests/api_resources/test_secrets.py index 6a54d36..680a9d7 100644 --- a/tests/api_resources/test_secrets.py +++ b/tests/api_resources/test_secrets.py @@ -41,6 +41,7 @@ def test_method_create_with_all_params(self, client: Gitpod) -> None: scope={ "organization_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "project_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "service_account_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "user_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", }, value="postgresql://user:pass@localhost:5432/db", @@ -86,6 +87,7 @@ def test_method_list_with_all_params(self, client: Gitpod) -> None: "scope": { "organization_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "project_id": "b0e12f6c-4c67-429d-a4a6-d9838b5da047", + "service_account_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "user_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", }, }, @@ -252,6 +254,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGitpod) -> scope={ "organization_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "project_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + "service_account_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "user_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", }, value="postgresql://user:pass@localhost:5432/db", @@ -297,6 +300,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncGitpod) -> N "scope": { "organization_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "project_id": "b0e12f6c-4c67-429d-a4a6-d9838b5da047", + "service_account_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", "user_id": "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", }, }, From a4c7479eeb85d549fbe481ab69a8f33e72027e81 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:08:30 +0000 Subject: [PATCH 12/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index a1fa096..6535584 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-8a0189eebf423961bd98075dab28db4377c3a939da1dcf237d1b3958724e6dcf.yml -openapi_spec_hash: 48004e7605cf720584e822ec11a2eb8b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-e5c85c471bd3b69f4237abeba5f04212621408955a347de366cfd294a05b2b80.yml +openapi_spec_hash: 2a09889ff17121827dad73c3d318a921 config_hash: acfe8cf5d6e4b26387e4fefa3bff8409 From fab0fb5b56ccdaa9fd049e54875854ab6b0caf41 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 20:42:29 +0000 Subject: [PATCH 13/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6535584..2b051f4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-e5c85c471bd3b69f4237abeba5f04212621408955a347de366cfd294a05b2b80.yml -openapi_spec_hash: 2a09889ff17121827dad73c3d318a921 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-978712dd212465a9a1633be0a7981cfad4dd3836827e7d98eeac1a4f14224f94.yml +openapi_spec_hash: 2f9436918b0af97785377ce80b4b2782 config_hash: acfe8cf5d6e4b26387e4fefa3bff8409 From ddd18c09beb0f24e076818783d2dae09ca9b9f8b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:20:34 +0000 Subject: [PATCH 14/27] feat(api): add search, creator, and status filters to ListWorkflows --- .stats.yml | 4 ++-- src/gitpod/types/groups/resource_role.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2b051f4..ba16c23 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-978712dd212465a9a1633be0a7981cfad4dd3836827e7d98eeac1a4f14224f94.yml -openapi_spec_hash: 2f9436918b0af97785377ce80b4b2782 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-247b6e1af7faefcf2f0ad1f1a012b45fd566be64e313d92648311bac4cda8b8e.yml +openapi_spec_hash: 5eb6026ee5764b077a65c26e85e4a84a config_hash: acfe8cf5d6e4b26387e4fefa3bff8409 diff --git a/src/gitpod/types/groups/resource_role.py b/src/gitpod/types/groups/resource_role.py index cd2e164..33ecbb7 100644 --- a/src/gitpod/types/groups/resource_role.py +++ b/src/gitpod/types/groups/resource_role.py @@ -8,6 +8,7 @@ "RESOURCE_ROLE_UNSPECIFIED", "RESOURCE_ROLE_ORG_ADMIN", "RESOURCE_ROLE_ORG_MEMBER", + "RESOURCE_ROLE_ORG_RUNNERS_ADMIN", "RESOURCE_ROLE_GROUP_ADMIN", "RESOURCE_ROLE_GROUP_VIEWER", "RESOURCE_ROLE_USER_IDENTITY", From 9c8f7eadd38bc0326ecf1be48706003fa258257c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 16:59:40 +0000 Subject: [PATCH 15/27] feat(api): add ListSCMOrganizations endpoint --- .stats.yml | 8 +- api.md | 2 + src/gitpod/resources/runners/runners.py | 152 ++++++++++++++++++ src/gitpod/types/__init__.py | 4 + .../runner_list_scm_organizations_params.py | 20 +++ .../runner_list_scm_organizations_response.py | 28 ++++ tests/api_resources/test_runners.py | 79 +++++++++ 7 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 src/gitpod/types/runner_list_scm_organizations_params.py create mode 100644 src/gitpod/types/runner_list_scm_organizations_response.py diff --git a/.stats.yml b/.stats.yml index ba16c23..d64cbf2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 160 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-247b6e1af7faefcf2f0ad1f1a012b45fd566be64e313d92648311bac4cda8b8e.yml -openapi_spec_hash: 5eb6026ee5764b077a65c26e85e4a84a -config_hash: acfe8cf5d6e4b26387e4fefa3bff8409 +configured_endpoints: 161 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-baa13045a9492d958fc06db0dcee2fd99972435f8b9a707831cf4da8d84db194.yml +openapi_spec_hash: 5e7adb5d5cdf924eb7c0e4ddf1b81260 +config_hash: d930f7e17a525d153b810339251607b7 diff --git a/api.md b/api.md index 9b1a07d..ed7817a 100644 --- a/api.md +++ b/api.md @@ -568,6 +568,7 @@ from gitpod.types import ( RunnerCheckAuthenticationForHostResponse, RunnerCreateLogsTokenResponse, RunnerCreateRunnerTokenResponse, + RunnerListScmOrganizationsResponse, RunnerParseContextURLResponse, RunnerSearchRepositoriesResponse, ) @@ -583,6 +584,7 @@ Methods: - client.runners.check_authentication_for_host(\*\*params) -> RunnerCheckAuthenticationForHostResponse - client.runners.create_logs_token(\*\*params) -> RunnerCreateLogsTokenResponse - client.runners.create_runner_token(\*\*params) -> RunnerCreateRunnerTokenResponse +- client.runners.list_scm_organizations(\*\*params) -> RunnerListScmOrganizationsResponse - client.runners.parse_context_url(\*\*params) -> RunnerParseContextURLResponse - client.runners.search_repositories(\*\*params) -> RunnerSearchRepositoriesResponse diff --git a/src/gitpod/resources/runners/runners.py b/src/gitpod/resources/runners/runners.py index 1c445f8..0ba69c1 100644 --- a/src/gitpod/resources/runners/runners.py +++ b/src/gitpod/resources/runners/runners.py @@ -19,6 +19,7 @@ runner_parse_context_url_params, runner_create_runner_token_params, runner_search_repositories_params, + runner_list_scm_organizations_params, runner_check_authentication_for_host_params, ) from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given @@ -60,6 +61,7 @@ from ...types.runner_parse_context_url_response import RunnerParseContextURLResponse from ...types.runner_create_runner_token_response import RunnerCreateRunnerTokenResponse from ...types.runner_search_repositories_response import RunnerSearchRepositoriesResponse +from ...types.runner_list_scm_organizations_response import RunnerListScmOrganizationsResponse from ...types.runner_check_authentication_for_host_response import RunnerCheckAuthenticationForHostResponse __all__ = ["RunnersResource", "AsyncRunnersResource"] @@ -608,6 +610,75 @@ def create_runner_token( cast_to=RunnerCreateRunnerTokenResponse, ) + def list_scm_organizations( + self, + *, + token: str | Omit = omit, + page_size: int | Omit = omit, + runner_id: str | Omit = omit, + scm_host: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunnerListScmOrganizationsResponse: + """ + Lists SCM organizations the user belongs to. + + Use this method to: + + - Get all organizations for a user on a specific SCM host + - Check organization admin permissions for webhook creation + + ### Examples + + - List GitHub organizations: + + Lists all organizations the user belongs to on GitHub. + + ```yaml + runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + scmHost: "github.com" + ``` + + Args: + scm_host: The SCM host to list organizations from (e.g., "github.com", "gitlab.com") + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/gitpod.v1.RunnerService/ListSCMOrganizations", + body=maybe_transform( + { + "runner_id": runner_id, + "scm_host": scm_host, + }, + runner_list_scm_organizations_params.RunnerListScmOrganizationsParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "token": token, + "page_size": page_size, + }, + runner_list_scm_organizations_params.RunnerListScmOrganizationsParams, + ), + ), + cast_to=RunnerListScmOrganizationsResponse, + ) + def parse_context_url( self, *, @@ -1304,6 +1375,75 @@ async def create_runner_token( cast_to=RunnerCreateRunnerTokenResponse, ) + async def list_scm_organizations( + self, + *, + token: str | Omit = omit, + page_size: int | Omit = omit, + runner_id: str | Omit = omit, + scm_host: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunnerListScmOrganizationsResponse: + """ + Lists SCM organizations the user belongs to. + + Use this method to: + + - Get all organizations for a user on a specific SCM host + - Check organization admin permissions for webhook creation + + ### Examples + + - List GitHub organizations: + + Lists all organizations the user belongs to on GitHub. + + ```yaml + runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + scmHost: "github.com" + ``` + + Args: + scm_host: The SCM host to list organizations from (e.g., "github.com", "gitlab.com") + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/gitpod.v1.RunnerService/ListSCMOrganizations", + body=await async_maybe_transform( + { + "runner_id": runner_id, + "scm_host": scm_host, + }, + runner_list_scm_organizations_params.RunnerListScmOrganizationsParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "token": token, + "page_size": page_size, + }, + runner_list_scm_organizations_params.RunnerListScmOrganizationsParams, + ), + ), + cast_to=RunnerListScmOrganizationsResponse, + ) + async def parse_context_url( self, *, @@ -1483,6 +1623,9 @@ def __init__(self, runners: RunnersResource) -> None: self.create_runner_token = to_raw_response_wrapper( runners.create_runner_token, ) + self.list_scm_organizations = to_raw_response_wrapper( + runners.list_scm_organizations, + ) self.parse_context_url = to_raw_response_wrapper( runners.parse_context_url, ) @@ -1527,6 +1670,9 @@ def __init__(self, runners: AsyncRunnersResource) -> None: self.create_runner_token = async_to_raw_response_wrapper( runners.create_runner_token, ) + self.list_scm_organizations = async_to_raw_response_wrapper( + runners.list_scm_organizations, + ) self.parse_context_url = async_to_raw_response_wrapper( runners.parse_context_url, ) @@ -1571,6 +1717,9 @@ def __init__(self, runners: RunnersResource) -> None: self.create_runner_token = to_streamed_response_wrapper( runners.create_runner_token, ) + self.list_scm_organizations = to_streamed_response_wrapper( + runners.list_scm_organizations, + ) self.parse_context_url = to_streamed_response_wrapper( runners.parse_context_url, ) @@ -1615,6 +1764,9 @@ def __init__(self, runners: AsyncRunnersResource) -> None: self.create_runner_token = async_to_streamed_response_wrapper( runners.create_runner_token, ) + self.list_scm_organizations = async_to_streamed_response_wrapper( + runners.list_scm_organizations, + ) self.parse_context_url = async_to_streamed_response_wrapper( runners.parse_context_url, ) diff --git a/src/gitpod/types/__init__.py b/src/gitpod/types/__init__.py index 842817b..7306513 100644 --- a/src/gitpod/types/__init__.py +++ b/src/gitpod/types/__init__.py @@ -221,6 +221,7 @@ from .runner_search_repositories_response import RunnerSearchRepositoriesResponse as RunnerSearchRepositoriesResponse from .environment_create_logs_token_params import EnvironmentCreateLogsTokenParams as EnvironmentCreateLogsTokenParams from .project_prebuild_configuration_param import ProjectPrebuildConfigurationParam as ProjectPrebuildConfigurationParam +from .runner_list_scm_organizations_params import RunnerListScmOrganizationsParams as RunnerListScmOrganizationsParams from .user_get_authenticated_user_response import UserGetAuthenticatedUserResponse as UserGetAuthenticatedUserResponse from .environment_create_from_project_params import ( EnvironmentCreateFromProjectParams as EnvironmentCreateFromProjectParams, @@ -231,6 +232,9 @@ from .project_create_from_environment_params import ( ProjectCreateFromEnvironmentParams as ProjectCreateFromEnvironmentParams, ) +from .runner_list_scm_organizations_response import ( + RunnerListScmOrganizationsResponse as RunnerListScmOrganizationsResponse, +) from .environment_create_from_project_response import ( EnvironmentCreateFromProjectResponse as EnvironmentCreateFromProjectResponse, ) diff --git a/src/gitpod/types/runner_list_scm_organizations_params.py b/src/gitpod/types/runner_list_scm_organizations_params.py new file mode 100644 index 0000000..bd1f788 --- /dev/null +++ b/src/gitpod/types/runner_list_scm_organizations_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["RunnerListScmOrganizationsParams"] + + +class RunnerListScmOrganizationsParams(TypedDict, total=False): + token: str + + page_size: Annotated[int, PropertyInfo(alias="pageSize")] + + runner_id: Annotated[str, PropertyInfo(alias="runnerId")] + + scm_host: Annotated[str, PropertyInfo(alias="scmHost")] + """The SCM host to list organizations from (e.g., "github.com", "gitlab.com")""" diff --git a/src/gitpod/types/runner_list_scm_organizations_response.py b/src/gitpod/types/runner_list_scm_organizations_response.py new file mode 100644 index 0000000..8e9db07 --- /dev/null +++ b/src/gitpod/types/runner_list_scm_organizations_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["RunnerListScmOrganizationsResponse", "Organization"] + + +class Organization(BaseModel): + is_admin: Optional[bool] = FieldInfo(alias="isAdmin", default=None) + """ + Whether the user has admin permissions in this organization. Admin permissions + typically allow creating organization-level webhooks. + """ + + name: Optional[str] = None + """Organization name/slug (e.g., "gitpod-io")""" + + url: Optional[str] = None + """Organization URL (e.g., "https://github.com/gitpod-io")""" + + +class RunnerListScmOrganizationsResponse(BaseModel): + organizations: Optional[List[Organization]] = None + """List of organizations the user belongs to""" diff --git a/tests/api_resources/test_runners.py b/tests/api_resources/test_runners.py index 9ce4674..5cf72fe 100644 --- a/tests/api_resources/test_runners.py +++ b/tests/api_resources/test_runners.py @@ -17,6 +17,7 @@ RunnerParseContextURLResponse, RunnerCreateRunnerTokenResponse, RunnerSearchRepositoriesResponse, + RunnerListScmOrganizationsResponse, RunnerCheckAuthenticationForHostResponse, ) from gitpod.pagination import SyncRunnersPage, AsyncRunnersPage @@ -363,6 +364,45 @@ def test_streaming_response_create_runner_token(self, client: Gitpod) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_scm_organizations(self, client: Gitpod) -> None: + runner = client.runners.list_scm_organizations() + assert_matches_type(RunnerListScmOrganizationsResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_scm_organizations_with_all_params(self, client: Gitpod) -> None: + runner = client.runners.list_scm_organizations( + token="token", + page_size=0, + runner_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + scm_host="github.com", + ) + assert_matches_type(RunnerListScmOrganizationsResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list_scm_organizations(self, client: Gitpod) -> None: + response = client.runners.with_raw_response.list_scm_organizations() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + runner = response.parse() + assert_matches_type(RunnerListScmOrganizationsResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list_scm_organizations(self, client: Gitpod) -> None: + with client.runners.with_streaming_response.list_scm_organizations() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + runner = response.parse() + assert_matches_type(RunnerListScmOrganizationsResponse, runner, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_parse_context_url(self, client: Gitpod) -> None: @@ -786,6 +826,45 @@ async def test_streaming_response_create_runner_token(self, async_client: AsyncG assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_scm_organizations(self, async_client: AsyncGitpod) -> None: + runner = await async_client.runners.list_scm_organizations() + assert_matches_type(RunnerListScmOrganizationsResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_scm_organizations_with_all_params(self, async_client: AsyncGitpod) -> None: + runner = await async_client.runners.list_scm_organizations( + token="token", + page_size=0, + runner_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + scm_host="github.com", + ) + assert_matches_type(RunnerListScmOrganizationsResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list_scm_organizations(self, async_client: AsyncGitpod) -> None: + response = await async_client.runners.with_raw_response.list_scm_organizations() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + runner = await response.parse() + assert_matches_type(RunnerListScmOrganizationsResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list_scm_organizations(self, async_client: AsyncGitpod) -> None: + async with async_client.runners.with_streaming_response.list_scm_organizations() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + runner = await response.parse() + assert_matches_type(RunnerListScmOrganizationsResponse, runner, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_parse_context_url(self, async_client: AsyncGitpod) -> None: From 53dcf30cb41a6cbf30ce510b0b2d46cdd5895008 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:28:04 +0000 Subject: [PATCH 16/27] chore(internal): update `actions/checkout` version --- .github/workflows/ci.yml | 32 +++++++++++++++--------------- .github/workflows/publish-pypi.yml | 6 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a69c2e7..e4988ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,15 +2,15 @@ name: CI on: push: branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + - "generated" + - "codegen/**" + - "integrated/**" + - "stl-preview-head/**" + - "stl-preview-base/**" pull_request: branches-ignore: - - 'stl-preview-head/**' - - 'stl-preview-base/**' + - "stl-preview-head/**" + - "stl-preview-base/**" jobs: lint: @@ -19,15 +19,15 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/gitpod-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - name: Install Rye run: | curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + RYE_VERSION: "0.44.0" + RYE_INSTALL_OPTION: "--yes" - name: Install dependencies run: rye sync --all-features @@ -44,15 +44,15 @@ jobs: id-token: write runs-on: ${{ github.repository == 'stainless-sdks/gitpod-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - name: Install Rye run: | curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + RYE_VERSION: "0.44.0" + RYE_INSTALL_OPTION: "--yes" - name: Install dependencies run: rye sync --all-features @@ -81,15 +81,15 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/gitpod-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - name: Install Rye run: | curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.44.0' - RYE_INSTALL_OPTION: '--yes' + RYE_VERSION: "0.44.0" + RYE_INSTALL_OPTION: "--yes" - name: Bootstrap run: ./scripts/bootstrap diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 3343ab6..0c20e7d 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -17,19 +17,19 @@ jobs: id-token: write steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - name: Install Rye uses: eifinger/setup-rye@c694239a43768373e87d0103d7f547027a23f3c8 # v4 with: version: '0.44.0' - + - name: Sync dependencies run: rye sync - name: Build package run: rye build --clean - + - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1 with: From 70becd4cd142fac4fc839018d52fb4cb93e17834 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:01:09 +0000 Subject: [PATCH 17/27] feat: API for SCIM configuration management --- .stats.yml | 8 +- api.md | 23 + src/gitpod/pagination.py | 53 + .../resources/organizations/__init__.py | 14 + .../resources/organizations/organizations.py | 32 + .../organizations/scim_configurations.py | 909 ++++++++++++++++++ src/gitpod/types/organizations/__init__.py | 15 + .../types/organizations/scim_configuration.py | 37 + .../scim_configuration_create_params.py | 27 + .../scim_configuration_create_response.py | 19 + .../scim_configuration_delete_params.py | 14 + .../scim_configuration_list_params.py | 31 + ...m_configuration_regenerate_token_params.py | 17 + ...configuration_regenerate_token_response.py | 13 + .../scim_configuration_retrieve_params.py | 14 + .../scim_configuration_retrieve_response.py | 13 + .../scim_configuration_update_params.py | 24 + .../scim_configuration_update_response.py | 13 + .../organizations/test_scim_configurations.py | 501 ++++++++++ 19 files changed, 1773 insertions(+), 4 deletions(-) create mode 100644 src/gitpod/resources/organizations/scim_configurations.py create mode 100644 src/gitpod/types/organizations/scim_configuration.py create mode 100644 src/gitpod/types/organizations/scim_configuration_create_params.py create mode 100644 src/gitpod/types/organizations/scim_configuration_create_response.py create mode 100644 src/gitpod/types/organizations/scim_configuration_delete_params.py create mode 100644 src/gitpod/types/organizations/scim_configuration_list_params.py create mode 100644 src/gitpod/types/organizations/scim_configuration_regenerate_token_params.py create mode 100644 src/gitpod/types/organizations/scim_configuration_regenerate_token_response.py create mode 100644 src/gitpod/types/organizations/scim_configuration_retrieve_params.py create mode 100644 src/gitpod/types/organizations/scim_configuration_retrieve_response.py create mode 100644 src/gitpod/types/organizations/scim_configuration_update_params.py create mode 100644 src/gitpod/types/organizations/scim_configuration_update_response.py create mode 100644 tests/api_resources/organizations/test_scim_configurations.py diff --git a/.stats.yml b/.stats.yml index d64cbf2..c7d8a2f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 161 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-baa13045a9492d958fc06db0dcee2fd99972435f8b9a707831cf4da8d84db194.yml -openapi_spec_hash: 5e7adb5d5cdf924eb7c0e4ddf1b81260 -config_hash: d930f7e17a525d153b810339251607b7 +configured_endpoints: 167 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-40c6617e0b944168058135c9325d556b21c2edde19518090aefa542c76afcdb3.yml +openapi_spec_hash: 235404d99aa56e51b933261d195dc206 +config_hash: cec4ffca97dd72023c573623499bc35b diff --git a/api.md b/api.md index ed7817a..8c0b90a 100644 --- a/api.md +++ b/api.md @@ -438,6 +438,29 @@ Methods: - client.organizations.policies.retrieve(\*\*params) -> PolicyRetrieveResponse - client.organizations.policies.update(\*\*params) -> object +## ScimConfigurations + +Types: + +```python +from gitpod.types.organizations import ( + ScimConfiguration, + ScimConfigurationCreateResponse, + ScimConfigurationRetrieveResponse, + ScimConfigurationUpdateResponse, + ScimConfigurationRegenerateTokenResponse, +) +``` + +Methods: + +- client.organizations.scim_configurations.create(\*\*params) -> ScimConfigurationCreateResponse +- client.organizations.scim_configurations.retrieve(\*\*params) -> ScimConfigurationRetrieveResponse +- client.organizations.scim_configurations.update(\*\*params) -> ScimConfigurationUpdateResponse +- client.organizations.scim_configurations.list(\*\*params) -> SyncScimConfigurationsPage[ScimConfiguration] +- client.organizations.scim_configurations.delete(\*\*params) -> object +- client.organizations.scim_configurations.regenerate_token(\*\*params) -> ScimConfigurationRegenerateTokenResponse + ## SSOConfigurations Types: diff --git a/src/gitpod/pagination.py b/src/gitpod/pagination.py index c8d007f..0ffd69d 100644 --- a/src/gitpod/pagination.py +++ b/src/gitpod/pagination.py @@ -78,6 +78,9 @@ "RunnersPagePagination", "SyncRunnersPage", "AsyncRunnersPage", + "ScimConfigurationsPagePagination", + "SyncScimConfigurationsPage", + "AsyncScimConfigurationsPage", "SecretsPagePagination", "SyncSecretsPage", "AsyncSecretsPage", @@ -1251,6 +1254,56 @@ def next_page_info(self) -> Optional[PageInfo]: return PageInfo(params={"token": next_token}) +class ScimConfigurationsPagePagination(BaseModel): + next_token: Optional[str] = FieldInfo(alias="nextToken", default=None) + + +class SyncScimConfigurationsPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + pagination: Optional[ScimConfigurationsPagePagination] = None + scim_configurations: List[_T] = FieldInfo(alias="scimConfigurations") + + @override + def _get_page_items(self) -> List[_T]: + scim_configurations = self.scim_configurations + if not scim_configurations: + return [] + return scim_configurations + + @override + def next_page_info(self) -> Optional[PageInfo]: + next_token = None + if self.pagination is not None: + if self.pagination.next_token is not None: + next_token = self.pagination.next_token + if not next_token: + return None + + return PageInfo(params={"token": next_token}) + + +class AsyncScimConfigurationsPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + pagination: Optional[ScimConfigurationsPagePagination] = None + scim_configurations: List[_T] = FieldInfo(alias="scimConfigurations") + + @override + def _get_page_items(self) -> List[_T]: + scim_configurations = self.scim_configurations + if not scim_configurations: + return [] + return scim_configurations + + @override + def next_page_info(self) -> Optional[PageInfo]: + next_token = None + if self.pagination is not None: + if self.pagination.next_token is not None: + next_token = self.pagination.next_token + if not next_token: + return None + + return PageInfo(params={"token": next_token}) + + class SecretsPagePagination(BaseModel): next_token: Optional[str] = FieldInfo(alias="nextToken", default=None) diff --git a/src/gitpod/resources/organizations/__init__.py b/src/gitpod/resources/organizations/__init__.py index 84d6bde..21debec 100644 --- a/src/gitpod/resources/organizations/__init__.py +++ b/src/gitpod/resources/organizations/__init__.py @@ -40,6 +40,14 @@ SSOConfigurationsResourceWithStreamingResponse, AsyncSSOConfigurationsResourceWithStreamingResponse, ) +from .scim_configurations import ( + ScimConfigurationsResource, + AsyncScimConfigurationsResource, + ScimConfigurationsResourceWithRawResponse, + AsyncScimConfigurationsResourceWithRawResponse, + ScimConfigurationsResourceWithStreamingResponse, + AsyncScimConfigurationsResourceWithStreamingResponse, +) from .domain_verifications import ( DomainVerificationsResource, AsyncDomainVerificationsResource, @@ -74,6 +82,12 @@ "AsyncPoliciesResourceWithRawResponse", "PoliciesResourceWithStreamingResponse", "AsyncPoliciesResourceWithStreamingResponse", + "ScimConfigurationsResource", + "AsyncScimConfigurationsResource", + "ScimConfigurationsResourceWithRawResponse", + "AsyncScimConfigurationsResourceWithRawResponse", + "ScimConfigurationsResourceWithStreamingResponse", + "AsyncScimConfigurationsResourceWithStreamingResponse", "SSOConfigurationsResource", "AsyncSSOConfigurationsResource", "SSOConfigurationsResourceWithRawResponse", diff --git a/src/gitpod/resources/organizations/organizations.py b/src/gitpod/resources/organizations/organizations.py index c9de4ae..c45b8a5 100644 --- a/src/gitpod/resources/organizations/organizations.py +++ b/src/gitpod/resources/organizations/organizations.py @@ -60,6 +60,14 @@ SSOConfigurationsResourceWithStreamingResponse, AsyncSSOConfigurationsResourceWithStreamingResponse, ) +from .scim_configurations import ( + ScimConfigurationsResource, + AsyncScimConfigurationsResource, + ScimConfigurationsResourceWithRawResponse, + AsyncScimConfigurationsResourceWithRawResponse, + ScimConfigurationsResourceWithStreamingResponse, + AsyncScimConfigurationsResourceWithStreamingResponse, +) from .domain_verifications import ( DomainVerificationsResource, AsyncDomainVerificationsResource, @@ -96,6 +104,10 @@ def invites(self) -> InvitesResource: def policies(self) -> PoliciesResource: return PoliciesResource(self._client) + @cached_property + def scim_configurations(self) -> ScimConfigurationsResource: + return ScimConfigurationsResource(self._client) + @cached_property def sso_configurations(self) -> SSOConfigurationsResource: return SSOConfigurationsResource(self._client) @@ -682,6 +694,10 @@ def invites(self) -> AsyncInvitesResource: def policies(self) -> AsyncPoliciesResource: return AsyncPoliciesResource(self._client) + @cached_property + def scim_configurations(self) -> AsyncScimConfigurationsResource: + return AsyncScimConfigurationsResource(self._client) + @cached_property def sso_configurations(self) -> AsyncSSOConfigurationsResource: return AsyncSSOConfigurationsResource(self._client) @@ -1296,6 +1312,10 @@ def invites(self) -> InvitesResourceWithRawResponse: def policies(self) -> PoliciesResourceWithRawResponse: return PoliciesResourceWithRawResponse(self._organizations.policies) + @cached_property + def scim_configurations(self) -> ScimConfigurationsResourceWithRawResponse: + return ScimConfigurationsResourceWithRawResponse(self._organizations.scim_configurations) + @cached_property def sso_configurations(self) -> SSOConfigurationsResourceWithRawResponse: return SSOConfigurationsResourceWithRawResponse(self._organizations.sso_configurations) @@ -1346,6 +1366,10 @@ def invites(self) -> AsyncInvitesResourceWithRawResponse: def policies(self) -> AsyncPoliciesResourceWithRawResponse: return AsyncPoliciesResourceWithRawResponse(self._organizations.policies) + @cached_property + def scim_configurations(self) -> AsyncScimConfigurationsResourceWithRawResponse: + return AsyncScimConfigurationsResourceWithRawResponse(self._organizations.scim_configurations) + @cached_property def sso_configurations(self) -> AsyncSSOConfigurationsResourceWithRawResponse: return AsyncSSOConfigurationsResourceWithRawResponse(self._organizations.sso_configurations) @@ -1396,6 +1420,10 @@ def invites(self) -> InvitesResourceWithStreamingResponse: def policies(self) -> PoliciesResourceWithStreamingResponse: return PoliciesResourceWithStreamingResponse(self._organizations.policies) + @cached_property + def scim_configurations(self) -> ScimConfigurationsResourceWithStreamingResponse: + return ScimConfigurationsResourceWithStreamingResponse(self._organizations.scim_configurations) + @cached_property def sso_configurations(self) -> SSOConfigurationsResourceWithStreamingResponse: return SSOConfigurationsResourceWithStreamingResponse(self._organizations.sso_configurations) @@ -1446,6 +1474,10 @@ def invites(self) -> AsyncInvitesResourceWithStreamingResponse: def policies(self) -> AsyncPoliciesResourceWithStreamingResponse: return AsyncPoliciesResourceWithStreamingResponse(self._organizations.policies) + @cached_property + def scim_configurations(self) -> AsyncScimConfigurationsResourceWithStreamingResponse: + return AsyncScimConfigurationsResourceWithStreamingResponse(self._organizations.scim_configurations) + @cached_property def sso_configurations(self) -> AsyncSSOConfigurationsResourceWithStreamingResponse: return AsyncSSOConfigurationsResourceWithStreamingResponse(self._organizations.sso_configurations) diff --git a/src/gitpod/resources/organizations/scim_configurations.py b/src/gitpod/resources/organizations/scim_configurations.py new file mode 100644 index 0000000..529ae16 --- /dev/null +++ b/src/gitpod/resources/organizations/scim_configurations.py @@ -0,0 +1,909 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncScimConfigurationsPage, AsyncScimConfigurationsPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.organizations import ( + scim_configuration_list_params, + scim_configuration_create_params, + scim_configuration_delete_params, + scim_configuration_update_params, + scim_configuration_retrieve_params, + scim_configuration_regenerate_token_params, +) +from ...types.organizations.scim_configuration import ScimConfiguration +from ...types.organizations.scim_configuration_create_response import ScimConfigurationCreateResponse +from ...types.organizations.scim_configuration_update_response import ScimConfigurationUpdateResponse +from ...types.organizations.scim_configuration_retrieve_response import ScimConfigurationRetrieveResponse +from ...types.organizations.scim_configuration_regenerate_token_response import ScimConfigurationRegenerateTokenResponse + +__all__ = ["ScimConfigurationsResource", "AsyncScimConfigurationsResource"] + + +class ScimConfigurationsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ScimConfigurationsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/gitpod-io/gitpod-sdk-python#accessing-raw-response-data-eg-headers + """ + return ScimConfigurationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ScimConfigurationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/gitpod-io/gitpod-sdk-python#with_streaming_response + """ + return ScimConfigurationsResourceWithStreamingResponse(self) + + def create( + self, + *, + organization_id: str, + sso_configuration_id: str, + name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScimConfigurationCreateResponse: + """ + Creates a new SCIM configuration for automated user provisioning. + + Use this method to: + + - Set up SCIM 2.0 provisioning from an identity provider + - Generate a bearer token for SCIM API authentication + - Link SCIM provisioning to an existing SSO configuration + + ### Examples + + - Create basic SCIM configuration: + + Creates a SCIM configuration linked to an SSO provider. + + ```yaml + organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047" + ssoConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ``` + + Args: + organization_id: organization_id is the ID of the organization to create the SCIM configuration + for + + sso_configuration_id: sso_configuration_id is the SSO configuration to link (required for user + provisioning) + + name: name is a human-readable name for the SCIM configuration + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/gitpod.v1.OrganizationService/CreateSCIMConfiguration", + body=maybe_transform( + { + "organization_id": organization_id, + "sso_configuration_id": sso_configuration_id, + "name": name, + }, + scim_configuration_create_params.ScimConfigurationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScimConfigurationCreateResponse, + ) + + def retrieve( + self, + *, + scim_configuration_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScimConfigurationRetrieveResponse: + """ + Retrieves a specific SCIM configuration. + + Use this method to: + + - View SCIM configuration details + - Check if SCIM is enabled + - Verify SSO linkage + + ### Examples + + - Get SCIM configuration: + + Retrieves details of a specific SCIM configuration. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ``` + + Args: + scim_configuration_id: scim_configuration_id is the ID of the SCIM configuration to get + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/gitpod.v1.OrganizationService/GetSCIMConfiguration", + body=maybe_transform( + {"scim_configuration_id": scim_configuration_id}, + scim_configuration_retrieve_params.ScimConfigurationRetrieveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScimConfigurationRetrieveResponse, + ) + + def update( + self, + *, + scim_configuration_id: str, + enabled: Optional[bool] | Omit = omit, + name: Optional[str] | Omit = omit, + sso_configuration_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScimConfigurationUpdateResponse: + """ + Updates a SCIM configuration. + + Use this method to: + + - Enable or disable SCIM provisioning + - Link or unlink SSO configuration + - Update configuration name + + ### Examples + + - Disable SCIM: + + Disables SCIM provisioning. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + enabled: false + ``` + + - Link to SSO: + + Links SCIM configuration to an SSO provider. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ssoConfigurationId: "f53d2330-3795-4c5d-a1f3-453121af9c60" + ``` + + Args: + scim_configuration_id: scim_configuration_id is the ID of the SCIM configuration to update + + enabled: enabled controls whether SCIM provisioning is active + + name: name is a human-readable name for the SCIM configuration + + sso_configuration_id: sso_configuration_id is the SSO configuration to link + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/gitpod.v1.OrganizationService/UpdateSCIMConfiguration", + body=maybe_transform( + { + "scim_configuration_id": scim_configuration_id, + "enabled": enabled, + "name": name, + "sso_configuration_id": sso_configuration_id, + }, + scim_configuration_update_params.ScimConfigurationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScimConfigurationUpdateResponse, + ) + + def list( + self, + *, + token: str | Omit = omit, + page_size: int | Omit = omit, + pagination: scim_configuration_list_params.Pagination | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SyncScimConfigurationsPage[ScimConfiguration]: + """ + Lists SCIM configurations for an organization. + + Use this method to: + + - View all SCIM configurations + - Monitor provisioning status + - Audit SCIM settings + + ### Examples + + - List SCIM configurations: + + Shows all SCIM configurations for an organization. + + ```yaml + pagination: + pageSize: 20 + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/gitpod.v1.OrganizationService/ListSCIMConfigurations", + page=SyncScimConfigurationsPage[ScimConfiguration], + body=maybe_transform( + {"pagination": pagination}, scim_configuration_list_params.ScimConfigurationListParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "token": token, + "page_size": page_size, + }, + scim_configuration_list_params.ScimConfigurationListParams, + ), + ), + model=ScimConfiguration, + method="post", + ) + + def delete( + self, + *, + scim_configuration_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Removes a SCIM configuration from an organization. + + Use this method to: + + - Disable SCIM provisioning completely + - Remove unused configurations + - Clean up after migration + + ### Examples + + - Delete SCIM configuration: + + Removes a specific SCIM configuration. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ``` + + Args: + scim_configuration_id: scim_configuration_id is the ID of the SCIM configuration to delete + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/gitpod.v1.OrganizationService/DeleteSCIMConfiguration", + body=maybe_transform( + {"scim_configuration_id": scim_configuration_id}, + scim_configuration_delete_params.ScimConfigurationDeleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def regenerate_token( + self, + *, + scim_configuration_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScimConfigurationRegenerateTokenResponse: + """ + Regenerates the bearer token for a SCIM configuration. + + Use this method to: + + - Rotate SCIM credentials + - Recover from token compromise + - Update IdP configuration + + ### Examples + + - Regenerate token: + + Creates a new bearer token, invalidating the old one. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ``` + + Args: + scim_configuration_id: scim_configuration_id is the ID of the SCIM configuration to regenerate token + for + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/gitpod.v1.OrganizationService/RegenerateSCIMToken", + body=maybe_transform( + {"scim_configuration_id": scim_configuration_id}, + scim_configuration_regenerate_token_params.ScimConfigurationRegenerateTokenParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScimConfigurationRegenerateTokenResponse, + ) + + +class AsyncScimConfigurationsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncScimConfigurationsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/gitpod-io/gitpod-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncScimConfigurationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncScimConfigurationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/gitpod-io/gitpod-sdk-python#with_streaming_response + """ + return AsyncScimConfigurationsResourceWithStreamingResponse(self) + + async def create( + self, + *, + organization_id: str, + sso_configuration_id: str, + name: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScimConfigurationCreateResponse: + """ + Creates a new SCIM configuration for automated user provisioning. + + Use this method to: + + - Set up SCIM 2.0 provisioning from an identity provider + - Generate a bearer token for SCIM API authentication + - Link SCIM provisioning to an existing SSO configuration + + ### Examples + + - Create basic SCIM configuration: + + Creates a SCIM configuration linked to an SSO provider. + + ```yaml + organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047" + ssoConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ``` + + Args: + organization_id: organization_id is the ID of the organization to create the SCIM configuration + for + + sso_configuration_id: sso_configuration_id is the SSO configuration to link (required for user + provisioning) + + name: name is a human-readable name for the SCIM configuration + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/gitpod.v1.OrganizationService/CreateSCIMConfiguration", + body=await async_maybe_transform( + { + "organization_id": organization_id, + "sso_configuration_id": sso_configuration_id, + "name": name, + }, + scim_configuration_create_params.ScimConfigurationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScimConfigurationCreateResponse, + ) + + async def retrieve( + self, + *, + scim_configuration_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScimConfigurationRetrieveResponse: + """ + Retrieves a specific SCIM configuration. + + Use this method to: + + - View SCIM configuration details + - Check if SCIM is enabled + - Verify SSO linkage + + ### Examples + + - Get SCIM configuration: + + Retrieves details of a specific SCIM configuration. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ``` + + Args: + scim_configuration_id: scim_configuration_id is the ID of the SCIM configuration to get + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/gitpod.v1.OrganizationService/GetSCIMConfiguration", + body=await async_maybe_transform( + {"scim_configuration_id": scim_configuration_id}, + scim_configuration_retrieve_params.ScimConfigurationRetrieveParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScimConfigurationRetrieveResponse, + ) + + async def update( + self, + *, + scim_configuration_id: str, + enabled: Optional[bool] | Omit = omit, + name: Optional[str] | Omit = omit, + sso_configuration_id: Optional[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScimConfigurationUpdateResponse: + """ + Updates a SCIM configuration. + + Use this method to: + + - Enable or disable SCIM provisioning + - Link or unlink SSO configuration + - Update configuration name + + ### Examples + + - Disable SCIM: + + Disables SCIM provisioning. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + enabled: false + ``` + + - Link to SSO: + + Links SCIM configuration to an SSO provider. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ssoConfigurationId: "f53d2330-3795-4c5d-a1f3-453121af9c60" + ``` + + Args: + scim_configuration_id: scim_configuration_id is the ID of the SCIM configuration to update + + enabled: enabled controls whether SCIM provisioning is active + + name: name is a human-readable name for the SCIM configuration + + sso_configuration_id: sso_configuration_id is the SSO configuration to link + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/gitpod.v1.OrganizationService/UpdateSCIMConfiguration", + body=await async_maybe_transform( + { + "scim_configuration_id": scim_configuration_id, + "enabled": enabled, + "name": name, + "sso_configuration_id": sso_configuration_id, + }, + scim_configuration_update_params.ScimConfigurationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScimConfigurationUpdateResponse, + ) + + def list( + self, + *, + token: str | Omit = omit, + page_size: int | Omit = omit, + pagination: scim_configuration_list_params.Pagination | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AsyncPaginator[ScimConfiguration, AsyncScimConfigurationsPage[ScimConfiguration]]: + """ + Lists SCIM configurations for an organization. + + Use this method to: + + - View all SCIM configurations + - Monitor provisioning status + - Audit SCIM settings + + ### Examples + + - List SCIM configurations: + + Shows all SCIM configurations for an organization. + + ```yaml + pagination: + pageSize: 20 + ``` + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/gitpod.v1.OrganizationService/ListSCIMConfigurations", + page=AsyncScimConfigurationsPage[ScimConfiguration], + body=maybe_transform( + {"pagination": pagination}, scim_configuration_list_params.ScimConfigurationListParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "token": token, + "page_size": page_size, + }, + scim_configuration_list_params.ScimConfigurationListParams, + ), + ), + model=ScimConfiguration, + method="post", + ) + + async def delete( + self, + *, + scim_configuration_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Removes a SCIM configuration from an organization. + + Use this method to: + + - Disable SCIM provisioning completely + - Remove unused configurations + - Clean up after migration + + ### Examples + + - Delete SCIM configuration: + + Removes a specific SCIM configuration. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ``` + + Args: + scim_configuration_id: scim_configuration_id is the ID of the SCIM configuration to delete + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/gitpod.v1.OrganizationService/DeleteSCIMConfiguration", + body=await async_maybe_transform( + {"scim_configuration_id": scim_configuration_id}, + scim_configuration_delete_params.ScimConfigurationDeleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def regenerate_token( + self, + *, + scim_configuration_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ScimConfigurationRegenerateTokenResponse: + """ + Regenerates the bearer token for a SCIM configuration. + + Use this method to: + + - Rotate SCIM credentials + - Recover from token compromise + - Update IdP configuration + + ### Examples + + - Regenerate token: + + Creates a new bearer token, invalidating the old one. + + ```yaml + scimConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + ``` + + Args: + scim_configuration_id: scim_configuration_id is the ID of the SCIM configuration to regenerate token + for + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/gitpod.v1.OrganizationService/RegenerateSCIMToken", + body=await async_maybe_transform( + {"scim_configuration_id": scim_configuration_id}, + scim_configuration_regenerate_token_params.ScimConfigurationRegenerateTokenParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ScimConfigurationRegenerateTokenResponse, + ) + + +class ScimConfigurationsResourceWithRawResponse: + def __init__(self, scim_configurations: ScimConfigurationsResource) -> None: + self._scim_configurations = scim_configurations + + self.create = to_raw_response_wrapper( + scim_configurations.create, + ) + self.retrieve = to_raw_response_wrapper( + scim_configurations.retrieve, + ) + self.update = to_raw_response_wrapper( + scim_configurations.update, + ) + self.list = to_raw_response_wrapper( + scim_configurations.list, + ) + self.delete = to_raw_response_wrapper( + scim_configurations.delete, + ) + self.regenerate_token = to_raw_response_wrapper( + scim_configurations.regenerate_token, + ) + + +class AsyncScimConfigurationsResourceWithRawResponse: + def __init__(self, scim_configurations: AsyncScimConfigurationsResource) -> None: + self._scim_configurations = scim_configurations + + self.create = async_to_raw_response_wrapper( + scim_configurations.create, + ) + self.retrieve = async_to_raw_response_wrapper( + scim_configurations.retrieve, + ) + self.update = async_to_raw_response_wrapper( + scim_configurations.update, + ) + self.list = async_to_raw_response_wrapper( + scim_configurations.list, + ) + self.delete = async_to_raw_response_wrapper( + scim_configurations.delete, + ) + self.regenerate_token = async_to_raw_response_wrapper( + scim_configurations.regenerate_token, + ) + + +class ScimConfigurationsResourceWithStreamingResponse: + def __init__(self, scim_configurations: ScimConfigurationsResource) -> None: + self._scim_configurations = scim_configurations + + self.create = to_streamed_response_wrapper( + scim_configurations.create, + ) + self.retrieve = to_streamed_response_wrapper( + scim_configurations.retrieve, + ) + self.update = to_streamed_response_wrapper( + scim_configurations.update, + ) + self.list = to_streamed_response_wrapper( + scim_configurations.list, + ) + self.delete = to_streamed_response_wrapper( + scim_configurations.delete, + ) + self.regenerate_token = to_streamed_response_wrapper( + scim_configurations.regenerate_token, + ) + + +class AsyncScimConfigurationsResourceWithStreamingResponse: + def __init__(self, scim_configurations: AsyncScimConfigurationsResource) -> None: + self._scim_configurations = scim_configurations + + self.create = async_to_streamed_response_wrapper( + scim_configurations.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + scim_configurations.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + scim_configurations.update, + ) + self.list = async_to_streamed_response_wrapper( + scim_configurations.list, + ) + self.delete = async_to_streamed_response_wrapper( + scim_configurations.delete, + ) + self.regenerate_token = async_to_streamed_response_wrapper( + scim_configurations.regenerate_token, + ) diff --git a/src/gitpod/types/organizations/__init__.py b/src/gitpod/types/organizations/__init__.py index ba4b2b5..60c60eb 100644 --- a/src/gitpod/types/organizations/__init__.py +++ b/src/gitpod/types/organizations/__init__.py @@ -6,6 +6,7 @@ from .custom_domain import CustomDomain as CustomDomain from .provider_type import ProviderType as ProviderType from .sso_configuration import SSOConfiguration as SSOConfiguration +from .scim_configuration import ScimConfiguration as ScimConfiguration from .crowd_strike_config import CrowdStrikeConfig as CrowdStrikeConfig from .domain_verification import DomainVerification as DomainVerification from .organization_invite import OrganizationInvite as OrganizationInvite @@ -30,20 +31,34 @@ from .custom_domain_retrieve_params import CustomDomainRetrieveParams as CustomDomainRetrieveParams from .custom_domain_update_response import CustomDomainUpdateResponse as CustomDomainUpdateResponse from .sso_configuration_list_params import SSOConfigurationListParams as SSOConfigurationListParams +from .scim_configuration_list_params import ScimConfigurationListParams as ScimConfigurationListParams from .custom_domain_retrieve_response import CustomDomainRetrieveResponse as CustomDomainRetrieveResponse from .domain_verification_list_params import DomainVerificationListParams as DomainVerificationListParams from .sso_configuration_create_params import SSOConfigurationCreateParams as SSOConfigurationCreateParams from .sso_configuration_delete_params import SSOConfigurationDeleteParams as SSOConfigurationDeleteParams from .sso_configuration_update_params import SSOConfigurationUpdateParams as SSOConfigurationUpdateParams +from .scim_configuration_create_params import ScimConfigurationCreateParams as ScimConfigurationCreateParams +from .scim_configuration_delete_params import ScimConfigurationDeleteParams as ScimConfigurationDeleteParams +from .scim_configuration_update_params import ScimConfigurationUpdateParams as ScimConfigurationUpdateParams from .domain_verification_create_params import DomainVerificationCreateParams as DomainVerificationCreateParams from .domain_verification_delete_params import DomainVerificationDeleteParams as DomainVerificationDeleteParams from .domain_verification_verify_params import DomainVerificationVerifyParams as DomainVerificationVerifyParams from .sso_configuration_create_response import SSOConfigurationCreateResponse as SSOConfigurationCreateResponse from .sso_configuration_retrieve_params import SSOConfigurationRetrieveParams as SSOConfigurationRetrieveParams +from .scim_configuration_create_response import ScimConfigurationCreateResponse as ScimConfigurationCreateResponse +from .scim_configuration_retrieve_params import ScimConfigurationRetrieveParams as ScimConfigurationRetrieveParams +from .scim_configuration_update_response import ScimConfigurationUpdateResponse as ScimConfigurationUpdateResponse from .domain_verification_create_response import DomainVerificationCreateResponse as DomainVerificationCreateResponse from .domain_verification_retrieve_params import DomainVerificationRetrieveParams as DomainVerificationRetrieveParams from .domain_verification_verify_response import DomainVerificationVerifyResponse as DomainVerificationVerifyResponse from .sso_configuration_retrieve_response import SSOConfigurationRetrieveResponse as SSOConfigurationRetrieveResponse +from .scim_configuration_retrieve_response import ScimConfigurationRetrieveResponse as ScimConfigurationRetrieveResponse from .domain_verification_retrieve_response import ( DomainVerificationRetrieveResponse as DomainVerificationRetrieveResponse, ) +from .scim_configuration_regenerate_token_params import ( + ScimConfigurationRegenerateTokenParams as ScimConfigurationRegenerateTokenParams, +) +from .scim_configuration_regenerate_token_response import ( + ScimConfigurationRegenerateTokenResponse as ScimConfigurationRegenerateTokenResponse, +) diff --git a/src/gitpod/types/organizations/scim_configuration.py b/src/gitpod/types/organizations/scim_configuration.py new file mode 100644 index 0000000..289db0f --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ScimConfiguration"] + + +class ScimConfiguration(BaseModel): + """SCIMConfiguration represents a SCIM 2.0 provisioning configuration""" + + id: str + """id is the unique identifier of the SCIM configuration""" + + created_at: datetime = FieldInfo(alias="createdAt") + """created_at is when the SCIM configuration was created""" + + organization_id: str = FieldInfo(alias="organizationId") + """ + organization_id is the ID of the organization this SCIM configuration belongs to + """ + + updated_at: datetime = FieldInfo(alias="updatedAt") + """updated_at is when the SCIM configuration was last updated""" + + enabled: Optional[bool] = None + """enabled indicates if SCIM provisioning is active""" + + name: Optional[str] = None + """name is a human-readable name for the SCIM configuration""" + + sso_configuration_id: Optional[str] = FieldInfo(alias="ssoConfigurationId", default=None) + """sso_configuration_id is the linked SSO configuration (optional)""" diff --git a/src/gitpod/types/organizations/scim_configuration_create_params.py b/src/gitpod/types/organizations/scim_configuration_create_params.py new file mode 100644 index 0000000..fae3380 --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_create_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ScimConfigurationCreateParams"] + + +class ScimConfigurationCreateParams(TypedDict, total=False): + organization_id: Required[Annotated[str, PropertyInfo(alias="organizationId")]] + """ + organization_id is the ID of the organization to create the SCIM configuration + for + """ + + sso_configuration_id: Required[Annotated[str, PropertyInfo(alias="ssoConfigurationId")]] + """ + sso_configuration_id is the SSO configuration to link (required for user + provisioning) + """ + + name: Optional[str] + """name is a human-readable name for the SCIM configuration""" diff --git a/src/gitpod/types/organizations/scim_configuration_create_response.py b/src/gitpod/types/organizations/scim_configuration_create_response.py new file mode 100644 index 0000000..95ba89b --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_create_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from .scim_configuration import ScimConfiguration + +__all__ = ["ScimConfigurationCreateResponse"] + + +class ScimConfigurationCreateResponse(BaseModel): + token: str + """ + token is the bearer token for SCIM API authentication. This is only returned + once during creation - store it securely. + """ + + scim_configuration: ScimConfiguration = FieldInfo(alias="scimConfiguration") + """scim_configuration is the created SCIM configuration""" diff --git a/src/gitpod/types/organizations/scim_configuration_delete_params.py b/src/gitpod/types/organizations/scim_configuration_delete_params.py new file mode 100644 index 0000000..51972cb --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_delete_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ScimConfigurationDeleteParams"] + + +class ScimConfigurationDeleteParams(TypedDict, total=False): + scim_configuration_id: Required[Annotated[str, PropertyInfo(alias="scimConfigurationId")]] + """scim_configuration_id is the ID of the SCIM configuration to delete""" diff --git a/src/gitpod/types/organizations/scim_configuration_list_params.py b/src/gitpod/types/organizations/scim_configuration_list_params.py new file mode 100644 index 0000000..199d3d3 --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_list_params.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ScimConfigurationListParams", "Pagination"] + + +class ScimConfigurationListParams(TypedDict, total=False): + token: str + + page_size: Annotated[int, PropertyInfo(alias="pageSize")] + + pagination: Pagination + + +class Pagination(TypedDict, total=False): + token: str + """ + Token for the next set of results that was returned as next_token of a + PaginationResponse + """ + + page_size: Annotated[int, PropertyInfo(alias="pageSize")] + """Page size is the maximum number of results to retrieve per page. Defaults to 25. + + Maximum 100. + """ diff --git a/src/gitpod/types/organizations/scim_configuration_regenerate_token_params.py b/src/gitpod/types/organizations/scim_configuration_regenerate_token_params.py new file mode 100644 index 0000000..b6cedad --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_regenerate_token_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ScimConfigurationRegenerateTokenParams"] + + +class ScimConfigurationRegenerateTokenParams(TypedDict, total=False): + scim_configuration_id: Required[Annotated[str, PropertyInfo(alias="scimConfigurationId")]] + """ + scim_configuration_id is the ID of the SCIM configuration to regenerate token + for + """ diff --git a/src/gitpod/types/organizations/scim_configuration_regenerate_token_response.py b/src/gitpod/types/organizations/scim_configuration_regenerate_token_response.py new file mode 100644 index 0000000..0af03b1 --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_regenerate_token_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel + +__all__ = ["ScimConfigurationRegenerateTokenResponse"] + + +class ScimConfigurationRegenerateTokenResponse(BaseModel): + token: str + """ + token is the new bearer token for SCIM API authentication. This invalidates the + previous token - store it securely. + """ diff --git a/src/gitpod/types/organizations/scim_configuration_retrieve_params.py b/src/gitpod/types/organizations/scim_configuration_retrieve_params.py new file mode 100644 index 0000000..f020f21 --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_retrieve_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ScimConfigurationRetrieveParams"] + + +class ScimConfigurationRetrieveParams(TypedDict, total=False): + scim_configuration_id: Required[Annotated[str, PropertyInfo(alias="scimConfigurationId")]] + """scim_configuration_id is the ID of the SCIM configuration to get""" diff --git a/src/gitpod/types/organizations/scim_configuration_retrieve_response.py b/src/gitpod/types/organizations/scim_configuration_retrieve_response.py new file mode 100644 index 0000000..55bb90a --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_retrieve_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from .scim_configuration import ScimConfiguration + +__all__ = ["ScimConfigurationRetrieveResponse"] + + +class ScimConfigurationRetrieveResponse(BaseModel): + scim_configuration: ScimConfiguration = FieldInfo(alias="scimConfiguration") + """scim_configuration is the SCIM configuration identified by the ID""" diff --git a/src/gitpod/types/organizations/scim_configuration_update_params.py b/src/gitpod/types/organizations/scim_configuration_update_params.py new file mode 100644 index 0000000..da91917 --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_update_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Optional +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ScimConfigurationUpdateParams"] + + +class ScimConfigurationUpdateParams(TypedDict, total=False): + scim_configuration_id: Required[Annotated[str, PropertyInfo(alias="scimConfigurationId")]] + """scim_configuration_id is the ID of the SCIM configuration to update""" + + enabled: Optional[bool] + """enabled controls whether SCIM provisioning is active""" + + name: Optional[str] + """name is a human-readable name for the SCIM configuration""" + + sso_configuration_id: Annotated[Optional[str], PropertyInfo(alias="ssoConfigurationId")] + """sso_configuration_id is the SSO configuration to link""" diff --git a/src/gitpod/types/organizations/scim_configuration_update_response.py b/src/gitpod/types/organizations/scim_configuration_update_response.py new file mode 100644 index 0000000..4c4d0ac --- /dev/null +++ b/src/gitpod/types/organizations/scim_configuration_update_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from .scim_configuration import ScimConfiguration + +__all__ = ["ScimConfigurationUpdateResponse"] + + +class ScimConfigurationUpdateResponse(BaseModel): + scim_configuration: ScimConfiguration = FieldInfo(alias="scimConfiguration") + """scim_configuration is the updated SCIM configuration""" diff --git a/tests/api_resources/organizations/test_scim_configurations.py b/tests/api_resources/organizations/test_scim_configurations.py new file mode 100644 index 0000000..16c6e25 --- /dev/null +++ b/tests/api_resources/organizations/test_scim_configurations.py @@ -0,0 +1,501 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gitpod import Gitpod, AsyncGitpod +from tests.utils import assert_matches_type +from gitpod.pagination import SyncScimConfigurationsPage, AsyncScimConfigurationsPage +from gitpod.types.organizations import ( + ScimConfiguration, + ScimConfigurationCreateResponse, + ScimConfigurationUpdateResponse, + ScimConfigurationRetrieveResponse, + ScimConfigurationRegenerateTokenResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestScimConfigurations: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gitpod) -> None: + scim_configuration = client.organizations.scim_configurations.create( + organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", + sso_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(ScimConfigurationCreateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gitpod) -> None: + scim_configuration = client.organizations.scim_configurations.create( + organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", + sso_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + name="name", + ) + assert_matches_type(ScimConfigurationCreateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gitpod) -> None: + response = client.organizations.scim_configurations.with_raw_response.create( + organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", + sso_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = response.parse() + assert_matches_type(ScimConfigurationCreateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gitpod) -> None: + with client.organizations.scim_configurations.with_streaming_response.create( + organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", + sso_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = response.parse() + assert_matches_type(ScimConfigurationCreateResponse, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_retrieve(self, client: Gitpod) -> None: + scim_configuration = client.organizations.scim_configurations.retrieve( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(ScimConfigurationRetrieveResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: Gitpod) -> None: + response = client.organizations.scim_configurations.with_raw_response.retrieve( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = response.parse() + assert_matches_type(ScimConfigurationRetrieveResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: Gitpod) -> None: + with client.organizations.scim_configurations.with_streaming_response.retrieve( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = response.parse() + assert_matches_type(ScimConfigurationRetrieveResponse, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Gitpod) -> None: + scim_configuration = client.organizations.scim_configurations.update( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(ScimConfigurationUpdateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Gitpod) -> None: + scim_configuration = client.organizations.scim_configurations.update( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + enabled=False, + name="name", + sso_configuration_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ScimConfigurationUpdateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Gitpod) -> None: + response = client.organizations.scim_configurations.with_raw_response.update( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = response.parse() + assert_matches_type(ScimConfigurationUpdateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Gitpod) -> None: + with client.organizations.scim_configurations.with_streaming_response.update( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = response.parse() + assert_matches_type(ScimConfigurationUpdateResponse, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Gitpod) -> None: + scim_configuration = client.organizations.scim_configurations.list() + assert_matches_type(SyncScimConfigurationsPage[ScimConfiguration], scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Gitpod) -> None: + scim_configuration = client.organizations.scim_configurations.list( + token="token", + page_size=0, + pagination={ + "token": "token", + "page_size": 20, + }, + ) + assert_matches_type(SyncScimConfigurationsPage[ScimConfiguration], scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Gitpod) -> None: + response = client.organizations.scim_configurations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = response.parse() + assert_matches_type(SyncScimConfigurationsPage[ScimConfiguration], scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Gitpod) -> None: + with client.organizations.scim_configurations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = response.parse() + assert_matches_type(SyncScimConfigurationsPage[ScimConfiguration], scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gitpod) -> None: + scim_configuration = client.organizations.scim_configurations.delete( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(object, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gitpod) -> None: + response = client.organizations.scim_configurations.with_raw_response.delete( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = response.parse() + assert_matches_type(object, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gitpod) -> None: + with client.organizations.scim_configurations.with_streaming_response.delete( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = response.parse() + assert_matches_type(object, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_regenerate_token(self, client: Gitpod) -> None: + scim_configuration = client.organizations.scim_configurations.regenerate_token( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(ScimConfigurationRegenerateTokenResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_regenerate_token(self, client: Gitpod) -> None: + response = client.organizations.scim_configurations.with_raw_response.regenerate_token( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = response.parse() + assert_matches_type(ScimConfigurationRegenerateTokenResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_regenerate_token(self, client: Gitpod) -> None: + with client.organizations.scim_configurations.with_streaming_response.regenerate_token( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = response.parse() + assert_matches_type(ScimConfigurationRegenerateTokenResponse, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncScimConfigurations: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGitpod) -> None: + scim_configuration = await async_client.organizations.scim_configurations.create( + organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", + sso_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(ScimConfigurationCreateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGitpod) -> None: + scim_configuration = await async_client.organizations.scim_configurations.create( + organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", + sso_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + name="name", + ) + assert_matches_type(ScimConfigurationCreateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGitpod) -> None: + response = await async_client.organizations.scim_configurations.with_raw_response.create( + organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", + sso_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = await response.parse() + assert_matches_type(ScimConfigurationCreateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGitpod) -> None: + async with async_client.organizations.scim_configurations.with_streaming_response.create( + organization_id="b0e12f6c-4c67-429d-a4a6-d9838b5da047", + sso_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = await response.parse() + assert_matches_type(ScimConfigurationCreateResponse, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncGitpod) -> None: + scim_configuration = await async_client.organizations.scim_configurations.retrieve( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(ScimConfigurationRetrieveResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncGitpod) -> None: + response = await async_client.organizations.scim_configurations.with_raw_response.retrieve( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = await response.parse() + assert_matches_type(ScimConfigurationRetrieveResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncGitpod) -> None: + async with async_client.organizations.scim_configurations.with_streaming_response.retrieve( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = await response.parse() + assert_matches_type(ScimConfigurationRetrieveResponse, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncGitpod) -> None: + scim_configuration = await async_client.organizations.scim_configurations.update( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(ScimConfigurationUpdateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncGitpod) -> None: + scim_configuration = await async_client.organizations.scim_configurations.update( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + enabled=False, + name="name", + sso_configuration_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(ScimConfigurationUpdateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncGitpod) -> None: + response = await async_client.organizations.scim_configurations.with_raw_response.update( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = await response.parse() + assert_matches_type(ScimConfigurationUpdateResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncGitpod) -> None: + async with async_client.organizations.scim_configurations.with_streaming_response.update( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = await response.parse() + assert_matches_type(ScimConfigurationUpdateResponse, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncGitpod) -> None: + scim_configuration = await async_client.organizations.scim_configurations.list() + assert_matches_type(AsyncScimConfigurationsPage[ScimConfiguration], scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncGitpod) -> None: + scim_configuration = await async_client.organizations.scim_configurations.list( + token="token", + page_size=0, + pagination={ + "token": "token", + "page_size": 20, + }, + ) + assert_matches_type(AsyncScimConfigurationsPage[ScimConfiguration], scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncGitpod) -> None: + response = await async_client.organizations.scim_configurations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = await response.parse() + assert_matches_type(AsyncScimConfigurationsPage[ScimConfiguration], scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncGitpod) -> None: + async with async_client.organizations.scim_configurations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = await response.parse() + assert_matches_type(AsyncScimConfigurationsPage[ScimConfiguration], scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGitpod) -> None: + scim_configuration = await async_client.organizations.scim_configurations.delete( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(object, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGitpod) -> None: + response = await async_client.organizations.scim_configurations.with_raw_response.delete( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = await response.parse() + assert_matches_type(object, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGitpod) -> None: + async with async_client.organizations.scim_configurations.with_streaming_response.delete( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = await response.parse() + assert_matches_type(object, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_regenerate_token(self, async_client: AsyncGitpod) -> None: + scim_configuration = await async_client.organizations.scim_configurations.regenerate_token( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(ScimConfigurationRegenerateTokenResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_regenerate_token(self, async_client: AsyncGitpod) -> None: + response = await async_client.organizations.scim_configurations.with_raw_response.regenerate_token( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + scim_configuration = await response.parse() + assert_matches_type(ScimConfigurationRegenerateTokenResponse, scim_configuration, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_regenerate_token(self, async_client: AsyncGitpod) -> None: + async with async_client.organizations.scim_configurations.with_streaming_response.regenerate_token( + scim_configuration_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + scim_configuration = await response.parse() + assert_matches_type(ScimConfigurationRegenerateTokenResponse, scim_configuration, path=["response"]) + + assert cast(Any, response.is_closed) is True From 87180158231c2ddc56bf7202be6c9dbc4b2ec3e2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 08:02:26 +0000 Subject: [PATCH 18/27] codegen metadata --- .stats.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index c7d8a2f..c836904 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 167 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-40c6617e0b944168058135c9325d556b21c2edde19518090aefa542c76afcdb3.yml -openapi_spec_hash: 235404d99aa56e51b933261d195dc206 -config_hash: cec4ffca97dd72023c573623499bc35b +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-041f3ba8dd0f5670e5fe37a61d725bd162d53c10c768bd3aa9b95ddc6d43926a.yml +openapi_spec_hash: 770ce1f0289034006cbe5279535dbbf3 +config_hash: 21bab2a4731f1e55c0a2c62588576282 From 9cd272f98f2215b834a841ca34c52ce04fd2898e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 11:34:35 +0000 Subject: [PATCH 19/27] feat(automations): add before_snapshot trigger type --- .stats.yml | 4 ++-- src/gitpod/types/runner_capability.py | 1 + src/gitpod/types/shared/automation_trigger.py | 6 +++++- src/gitpod/types/shared_params/automation_trigger.py | 6 +++++- .../api_resources/environments/automations/test_services.py | 4 ++++ tests/api_resources/environments/automations/test_tasks.py | 4 ++++ tests/api_resources/test_environments.py | 4 ++++ 7 files changed, 25 insertions(+), 4 deletions(-) diff --git a/.stats.yml b/.stats.yml index c836904..0df29b8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 167 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-041f3ba8dd0f5670e5fe37a61d725bd162d53c10c768bd3aa9b95ddc6d43926a.yml -openapi_spec_hash: 770ce1f0289034006cbe5279535dbbf3 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-a7829781a00de0869ad8536dcd67bdbbf224a42f5f3f922065a44e73b0534bbf.yml +openapi_spec_hash: 59381e16e5de8d6de09f92abc0433e02 config_hash: 21bab2a4731f1e55c0a2c62588576282 diff --git a/src/gitpod/types/runner_capability.py b/src/gitpod/types/runner_capability.py index b63ac78..36bb1f9 100644 --- a/src/gitpod/types/runner_capability.py +++ b/src/gitpod/types/runner_capability.py @@ -12,4 +12,5 @@ "RUNNER_CAPABILITY_ALLOW_ENV_TOKEN_POPULATION", "RUNNER_CAPABILITY_DEFAULT_DEV_CONTAINER_IMAGE", "RUNNER_CAPABILITY_ENVIRONMENT_SNAPSHOT", + "RUNNER_CAPABILITY_PREBUILDS_BEFORE_SNAPSHOT_TRIGGER", ] diff --git a/src/gitpod/types/shared/automation_trigger.py b/src/gitpod/types/shared/automation_trigger.py index c21e923..8ffd732 100644 --- a/src/gitpod/types/shared/automation_trigger.py +++ b/src/gitpod/types/shared/automation_trigger.py @@ -18,9 +18,13 @@ class AutomationTrigger(BaseModel): The `post_environment_start` field indicates that the automation should be triggered after the environment has started (devcontainer ready). The `post_devcontainer_start` field indicates that the automation should be triggered after the dev container has started. The `prebuild` field starts the automation during a prebuild of an environment. This phase does not have user secrets available. - Note: The prebuild trigger can only be used with tasks, not services. + The `before_snapshot` field triggers the automation after all prebuild tasks complete but before the snapshot is taken. + This is used for tasks that need to run last during prebuilds, such as IDE warmup. + Note: The prebuild and before_snapshot triggers can only be used with tasks, not services. """ + before_snapshot: Optional[bool] = FieldInfo(alias="beforeSnapshot", default=None) + manual: Optional[bool] = None post_devcontainer_start: Optional[bool] = FieldInfo(alias="postDevcontainerStart", default=None) diff --git a/src/gitpod/types/shared_params/automation_trigger.py b/src/gitpod/types/shared_params/automation_trigger.py index 0c840fe..27dc462 100644 --- a/src/gitpod/types/shared_params/automation_trigger.py +++ b/src/gitpod/types/shared_params/automation_trigger.py @@ -18,9 +18,13 @@ class AutomationTrigger(TypedDict, total=False): The `post_environment_start` field indicates that the automation should be triggered after the environment has started (devcontainer ready). The `post_devcontainer_start` field indicates that the automation should be triggered after the dev container has started. The `prebuild` field starts the automation during a prebuild of an environment. This phase does not have user secrets available. - Note: The prebuild trigger can only be used with tasks, not services. + The `before_snapshot` field triggers the automation after all prebuild tasks complete but before the snapshot is taken. + This is used for tasks that need to run last during prebuilds, such as IDE warmup. + Note: The prebuild and before_snapshot triggers can only be used with tasks, not services. """ + before_snapshot: Annotated[bool, PropertyInfo(alias="beforeSnapshot")] + manual: bool post_devcontainer_start: Annotated[bool, PropertyInfo(alias="postDevcontainerStart")] diff --git a/tests/api_resources/environments/automations/test_services.py b/tests/api_resources/environments/automations/test_services.py index 650c04b..c0db2dc 100644 --- a/tests/api_resources/environments/automations/test_services.py +++ b/tests/api_resources/environments/automations/test_services.py @@ -46,6 +46,7 @@ def test_method_create_with_all_params(self, client: Gitpod) -> None: "role": "SERVICE_ROLE_UNSPECIFIED", "triggered_by": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, @@ -157,6 +158,7 @@ def test_method_update_with_all_params(self, client: Gitpod) -> None: "triggered_by": { "trigger": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, @@ -404,6 +406,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGitpod) -> "role": "SERVICE_ROLE_UNSPECIFIED", "triggered_by": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, @@ -515,6 +518,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGitpod) -> "triggered_by": { "trigger": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, diff --git a/tests/api_resources/environments/automations/test_tasks.py b/tests/api_resources/environments/automations/test_tasks.py index 057d1a1..289d4f4 100644 --- a/tests/api_resources/environments/automations/test_tasks.py +++ b/tests/api_resources/environments/automations/test_tasks.py @@ -47,6 +47,7 @@ def test_method_create_with_all_params(self, client: Gitpod) -> None: "reference": "build", "triggered_by": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, @@ -151,6 +152,7 @@ def test_method_update_with_all_params(self, client: Gitpod) -> None: "triggered_by": { "trigger": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, @@ -349,6 +351,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGitpod) -> "reference": "build", "triggered_by": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, @@ -453,6 +456,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncGitpod) -> "triggered_by": { "trigger": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, diff --git a/tests/api_resources/test_environments.py b/tests/api_resources/test_environments.py index 127cc4b..b2a37f6 100644 --- a/tests/api_resources/test_environments.py +++ b/tests/api_resources/test_environments.py @@ -43,6 +43,7 @@ def test_method_create_with_all_params(self, client: Gitpod) -> None: "session": "session", "trigger_filter": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, @@ -394,6 +395,7 @@ def test_method_create_from_project_with_all_params(self, client: Gitpod) -> Non "session": "session", "trigger_filter": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, @@ -697,6 +699,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGitpod) -> "session": "session", "trigger_filter": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, @@ -1048,6 +1051,7 @@ async def test_method_create_from_project_with_all_params(self, async_client: As "session": "session", "trigger_filter": [ { + "before_snapshot": True, "manual": True, "post_devcontainer_start": True, "post_environment_start": True, From 78c0bddc838e217007729d723283f9e9cd04d9a2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 03:19:40 +0000 Subject: [PATCH 20/27] feat: [backend] Adding direct_share field to groups --- .stats.yml | 4 ++-- src/gitpod/types/group.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0df29b8..52fe54a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 167 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-a7829781a00de0869ad8536dcd67bdbbf224a42f5f3f922065a44e73b0534bbf.yml -openapi_spec_hash: 59381e16e5de8d6de09f92abc0433e02 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-dfd13e2785a9e0e662f79aab0c03d45144d803fedd87510ec27791b79ede6162.yml +openapi_spec_hash: 8dcab0c2c65f9eb778e9b2cf3c44bf5c config_hash: 21bab2a4731f1e55c0a2c62588576282 diff --git a/src/gitpod/types/group.py b/src/gitpod/types/group.py index ac727d0..1b70f57 100644 --- a/src/gitpod/types/group.py +++ b/src/gitpod/types/group.py @@ -107,6 +107,12 @@ class Group(BaseModel): description: Optional[str] = None + direct_share: Optional[bool] = FieldInfo(alias="directShare", default=None) + """ + direct_share indicates that this group is used for direct user sharing on + resources. These groups are hidden from regular group listings. + """ + member_count: Optional[int] = FieldInfo(alias="memberCount", default=None) """member_count is the total number of members in this group""" From df1cf39a9c1b387c9e9313bfec68a97759762e0b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 05:13:42 +0000 Subject: [PATCH 21/27] feat: [api] Introduce RPCs to share resources with individual users --- .stats.yml | 8 +- api.md | 10 +- src/gitpod/resources/groups/__init__.py | 14 + src/gitpod/resources/groups/groups.py | 32 ++ .../resources/groups/role_assignments.py | 9 +- src/gitpod/resources/groups/shares.py | 441 ++++++++++++++++++ src/gitpod/types/__init__.py | 1 + src/gitpod/types/groups/__init__.py | 3 +- src/gitpod/types/groups/role_assignment.py | 2 +- .../groups/role_assignment_create_params.py | 2 +- .../groups/role_assignment_list_params.py | 2 +- .../types/groups/share_create_params.py | 29 ++ .../types/groups/share_delete_params.py | 25 + src/gitpod/types/shared/__init__.py | 1 + .../types/{groups => shared}/resource_role.py | 0 src/gitpod/types/shared_params/__init__.py | 1 + .../types/shared_params/resource_role.py | 61 +++ tests/api_resources/groups/test_shares.py | 181 +++++++ 18 files changed, 806 insertions(+), 16 deletions(-) create mode 100644 src/gitpod/resources/groups/shares.py create mode 100644 src/gitpod/types/groups/share_create_params.py create mode 100644 src/gitpod/types/groups/share_delete_params.py rename src/gitpod/types/{groups => shared}/resource_role.py (100%) create mode 100644 src/gitpod/types/shared_params/resource_role.py create mode 100644 tests/api_resources/groups/test_shares.py diff --git a/.stats.yml b/.stats.yml index 52fe54a..896d8f2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 167 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-dfd13e2785a9e0e662f79aab0c03d45144d803fedd87510ec27791b79ede6162.yml -openapi_spec_hash: 8dcab0c2c65f9eb778e9b2cf3c44bf5c -config_hash: 21bab2a4731f1e55c0a2c62588576282 +configured_endpoints: 169 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-6b80aebab53bff73bb7b87c5f91f98c6dd5db2d623b6c613aaa5e61252b74d75.yml +openapi_spec_hash: 9636e315ac739c1ab9cba65a7ead6559 +config_hash: 73893621fd64bbd87b86671decf334e6 diff --git a/api.md b/api.md index 8c0b90a..796ab4b 100644 --- a/api.md +++ b/api.md @@ -13,6 +13,7 @@ from gitpod.types import ( OrganizationTier, Principal, ProjectEnvironmentClass, + ResourceRole, ResourceType, RunsOn, SecretRef, @@ -302,7 +303,7 @@ Methods: Types: ```python -from gitpod.types.groups import ResourceRole, RoleAssignment, RoleAssignmentCreateResponse +from gitpod.types.groups import RoleAssignment, RoleAssignmentCreateResponse ``` Methods: @@ -311,6 +312,13 @@ Methods: - client.groups.role_assignments.list(\*\*params) -> SyncAssignmentsPage[RoleAssignment] - client.groups.role_assignments.delete(\*\*params) -> object +## Shares + +Methods: + +- client.groups.shares.create(\*\*params) -> object +- client.groups.shares.delete(\*\*params) -> object + # Identity Types: diff --git a/src/gitpod/resources/groups/__init__.py b/src/gitpod/resources/groups/__init__.py index 83c3111..363f4ea 100644 --- a/src/gitpod/resources/groups/__init__.py +++ b/src/gitpod/resources/groups/__init__.py @@ -8,6 +8,14 @@ GroupsResourceWithStreamingResponse, AsyncGroupsResourceWithStreamingResponse, ) +from .shares import ( + SharesResource, + AsyncSharesResource, + SharesResourceWithRawResponse, + AsyncSharesResourceWithRawResponse, + SharesResourceWithStreamingResponse, + AsyncSharesResourceWithStreamingResponse, +) from .memberships import ( MembershipsResource, AsyncMembershipsResource, @@ -38,6 +46,12 @@ "AsyncRoleAssignmentsResourceWithRawResponse", "RoleAssignmentsResourceWithStreamingResponse", "AsyncRoleAssignmentsResourceWithStreamingResponse", + "SharesResource", + "AsyncSharesResource", + "SharesResourceWithRawResponse", + "AsyncSharesResourceWithRawResponse", + "SharesResourceWithStreamingResponse", + "AsyncSharesResourceWithStreamingResponse", "GroupsResource", "AsyncGroupsResource", "GroupsResourceWithRawResponse", diff --git a/src/gitpod/resources/groups/groups.py b/src/gitpod/resources/groups/groups.py index 4a57994..bebd369 100644 --- a/src/gitpod/resources/groups/groups.py +++ b/src/gitpod/resources/groups/groups.py @@ -4,6 +4,14 @@ import httpx +from .shares import ( + SharesResource, + AsyncSharesResource, + SharesResourceWithRawResponse, + AsyncSharesResourceWithRawResponse, + SharesResourceWithStreamingResponse, + AsyncSharesResourceWithStreamingResponse, +) from ...types import ( group_list_params, group_create_params, @@ -56,6 +64,10 @@ def memberships(self) -> MembershipsResource: def role_assignments(self) -> RoleAssignmentsResource: return RoleAssignmentsResource(self._client) + @cached_property + def shares(self) -> SharesResource: + return SharesResource(self._client) + @cached_property def with_raw_response(self) -> GroupsResourceWithRawResponse: """ @@ -396,6 +408,10 @@ def memberships(self) -> AsyncMembershipsResource: def role_assignments(self) -> AsyncRoleAssignmentsResource: return AsyncRoleAssignmentsResource(self._client) + @cached_property + def shares(self) -> AsyncSharesResource: + return AsyncSharesResource(self._client) + @cached_property def with_raw_response(self) -> AsyncGroupsResourceWithRawResponse: """ @@ -755,6 +771,10 @@ def memberships(self) -> MembershipsResourceWithRawResponse: def role_assignments(self) -> RoleAssignmentsResourceWithRawResponse: return RoleAssignmentsResourceWithRawResponse(self._groups.role_assignments) + @cached_property + def shares(self) -> SharesResourceWithRawResponse: + return SharesResourceWithRawResponse(self._groups.shares) + class AsyncGroupsResourceWithRawResponse: def __init__(self, groups: AsyncGroupsResource) -> None: @@ -784,6 +804,10 @@ def memberships(self) -> AsyncMembershipsResourceWithRawResponse: def role_assignments(self) -> AsyncRoleAssignmentsResourceWithRawResponse: return AsyncRoleAssignmentsResourceWithRawResponse(self._groups.role_assignments) + @cached_property + def shares(self) -> AsyncSharesResourceWithRawResponse: + return AsyncSharesResourceWithRawResponse(self._groups.shares) + class GroupsResourceWithStreamingResponse: def __init__(self, groups: GroupsResource) -> None: @@ -813,6 +837,10 @@ def memberships(self) -> MembershipsResourceWithStreamingResponse: def role_assignments(self) -> RoleAssignmentsResourceWithStreamingResponse: return RoleAssignmentsResourceWithStreamingResponse(self._groups.role_assignments) + @cached_property + def shares(self) -> SharesResourceWithStreamingResponse: + return SharesResourceWithStreamingResponse(self._groups.shares) + class AsyncGroupsResourceWithStreamingResponse: def __init__(self, groups: AsyncGroupsResource) -> None: @@ -841,3 +869,7 @@ def memberships(self) -> AsyncMembershipsResourceWithStreamingResponse: @cached_property def role_assignments(self) -> AsyncRoleAssignmentsResourceWithStreamingResponse: return AsyncRoleAssignmentsResourceWithStreamingResponse(self._groups.role_assignments) + + @cached_property + def shares(self) -> AsyncSharesResourceWithStreamingResponse: + return AsyncSharesResourceWithStreamingResponse(self._groups.shares) diff --git a/src/gitpod/resources/groups/role_assignments.py b/src/gitpod/resources/groups/role_assignments.py index 754dad3..0dde1df 100644 --- a/src/gitpod/resources/groups/role_assignments.py +++ b/src/gitpod/resources/groups/role_assignments.py @@ -16,13 +16,8 @@ ) from ...pagination import SyncAssignmentsPage, AsyncAssignmentsPage from ..._base_client import AsyncPaginator, make_request_options -from ...types.groups import ( - ResourceRole, - role_assignment_list_params, - role_assignment_create_params, - role_assignment_delete_params, -) -from ...types.groups.resource_role import ResourceRole +from ...types.groups import role_assignment_list_params, role_assignment_create_params, role_assignment_delete_params +from ...types.shared.resource_role import ResourceRole from ...types.shared.resource_type import ResourceType from ...types.groups.role_assignment import RoleAssignment from ...types.groups.role_assignment_create_response import RoleAssignmentCreateResponse diff --git a/src/gitpod/resources/groups/shares.py b/src/gitpod/resources/groups/shares.py new file mode 100644 index 0000000..fc0c308 --- /dev/null +++ b/src/gitpod/resources/groups/shares.py @@ -0,0 +1,441 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.groups import share_create_params, share_delete_params +from ...types.shared.principal import Principal +from ...types.shared.resource_role import ResourceRole +from ...types.shared.resource_type import ResourceType + +__all__ = ["SharesResource", "AsyncSharesResource"] + + +class SharesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SharesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/gitpod-io/gitpod-sdk-python#accessing-raw-response-data-eg-headers + """ + return SharesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SharesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/gitpod-io/gitpod-sdk-python#with_streaming_response + """ + return SharesResourceWithStreamingResponse(self) + + def create( + self, + *, + principal: Principal | Omit = omit, + principal_id: str | Omit = omit, + resource_id: str | Omit = omit, + resource_type: ResourceType | Omit = omit, + role: ResourceRole | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Shares a resource directly with a principal (user or service account). + + Use this method to: + + - Grant a user or service account direct access to a runner, project, or other + resource + - Share resources without creating and managing groups manually + + ### Examples + + - Share a runner with a user: + + Grants admin access to a runner for a specific user. + + ```yaml + resourceType: RESOURCE_TYPE_RUNNER + resourceId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + principal: PRINCIPAL_USER + principalId: "f53d2330-3795-4c5d-a1f3-453121af9c60" + role: RESOURCE_ROLE_RUNNER_ADMIN + ``` + + - Share a runner with a service account: + + Grants user access to a runner for a service account. + + ```yaml + resourceType: RESOURCE_TYPE_RUNNER + resourceId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + principal: PRINCIPAL_SERVICE_ACCOUNT + principalId: "a1b2c3d4-5678-90ab-cdef-1234567890ab" + role: RESOURCE_ROLE_RUNNER_USER + ``` + + ### Authorization + + Requires admin role on the specific resource. + + Args: + principal: Type of principal to share with (user or service account) + + principal_id: ID of the principal (user or service account) to share with + + resource_id: ID of the resource to share + + resource_type: Type of resource to share (runner, project, etc.) + + role: Role to grant the principal on the resource + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/gitpod.v1.GroupService/ShareResourceWithPrincipal", + body=maybe_transform( + { + "principal": principal, + "principal_id": principal_id, + "resource_id": resource_id, + "resource_type": resource_type, + "role": role, + }, + share_create_params.ShareCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + def delete( + self, + *, + principal: Principal | Omit = omit, + principal_id: str | Omit = omit, + resource_id: str | Omit = omit, + resource_type: ResourceType | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Removes direct access for a principal (user or service account) from a resource. + + Use this method to: + + - Revoke a principal's direct access to a resource + - Remove sharing without affecting group-based access + + ### Examples + + - Remove user access from a runner: + + Revokes a user's direct access to a runner. + + ```yaml + resourceType: RESOURCE_TYPE_RUNNER + resourceId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + principal: PRINCIPAL_USER + principalId: "f53d2330-3795-4c5d-a1f3-453121af9c60" + ``` + + ### Authorization + + Requires admin role on the specific resource. + + Args: + principal: Type of principal to remove access from (user or service account) + + principal_id: ID of the principal (user or service account) to remove access from + + resource_id: ID of the resource to unshare + + resource_type: Type of resource to unshare + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/gitpod.v1.GroupService/UnshareResourceWithPrincipal", + body=maybe_transform( + { + "principal": principal, + "principal_id": principal_id, + "resource_id": resource_id, + "resource_type": resource_type, + }, + share_delete_params.ShareDeleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class AsyncSharesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSharesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/gitpod-io/gitpod-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncSharesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSharesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/gitpod-io/gitpod-sdk-python#with_streaming_response + """ + return AsyncSharesResourceWithStreamingResponse(self) + + async def create( + self, + *, + principal: Principal | Omit = omit, + principal_id: str | Omit = omit, + resource_id: str | Omit = omit, + resource_type: ResourceType | Omit = omit, + role: ResourceRole | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Shares a resource directly with a principal (user or service account). + + Use this method to: + + - Grant a user or service account direct access to a runner, project, or other + resource + - Share resources without creating and managing groups manually + + ### Examples + + - Share a runner with a user: + + Grants admin access to a runner for a specific user. + + ```yaml + resourceType: RESOURCE_TYPE_RUNNER + resourceId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + principal: PRINCIPAL_USER + principalId: "f53d2330-3795-4c5d-a1f3-453121af9c60" + role: RESOURCE_ROLE_RUNNER_ADMIN + ``` + + - Share a runner with a service account: + + Grants user access to a runner for a service account. + + ```yaml + resourceType: RESOURCE_TYPE_RUNNER + resourceId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + principal: PRINCIPAL_SERVICE_ACCOUNT + principalId: "a1b2c3d4-5678-90ab-cdef-1234567890ab" + role: RESOURCE_ROLE_RUNNER_USER + ``` + + ### Authorization + + Requires admin role on the specific resource. + + Args: + principal: Type of principal to share with (user or service account) + + principal_id: ID of the principal (user or service account) to share with + + resource_id: ID of the resource to share + + resource_type: Type of resource to share (runner, project, etc.) + + role: Role to grant the principal on the resource + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/gitpod.v1.GroupService/ShareResourceWithPrincipal", + body=await async_maybe_transform( + { + "principal": principal, + "principal_id": principal_id, + "resource_id": resource_id, + "resource_type": resource_type, + "role": role, + }, + share_create_params.ShareCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + async def delete( + self, + *, + principal: Principal | Omit = omit, + principal_id: str | Omit = omit, + resource_id: str | Omit = omit, + resource_type: ResourceType | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> object: + """ + Removes direct access for a principal (user or service account) from a resource. + + Use this method to: + + - Revoke a principal's direct access to a resource + - Remove sharing without affecting group-based access + + ### Examples + + - Remove user access from a runner: + + Revokes a user's direct access to a runner. + + ```yaml + resourceType: RESOURCE_TYPE_RUNNER + resourceId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + principal: PRINCIPAL_USER + principalId: "f53d2330-3795-4c5d-a1f3-453121af9c60" + ``` + + ### Authorization + + Requires admin role on the specific resource. + + Args: + principal: Type of principal to remove access from (user or service account) + + principal_id: ID of the principal (user or service account) to remove access from + + resource_id: ID of the resource to unshare + + resource_type: Type of resource to unshare + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/gitpod.v1.GroupService/UnshareResourceWithPrincipal", + body=await async_maybe_transform( + { + "principal": principal, + "principal_id": principal_id, + "resource_id": resource_id, + "resource_type": resource_type, + }, + share_delete_params.ShareDeleteParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=object, + ) + + +class SharesResourceWithRawResponse: + def __init__(self, shares: SharesResource) -> None: + self._shares = shares + + self.create = to_raw_response_wrapper( + shares.create, + ) + self.delete = to_raw_response_wrapper( + shares.delete, + ) + + +class AsyncSharesResourceWithRawResponse: + def __init__(self, shares: AsyncSharesResource) -> None: + self._shares = shares + + self.create = async_to_raw_response_wrapper( + shares.create, + ) + self.delete = async_to_raw_response_wrapper( + shares.delete, + ) + + +class SharesResourceWithStreamingResponse: + def __init__(self, shares: SharesResource) -> None: + self._shares = shares + + self.create = to_streamed_response_wrapper( + shares.create, + ) + self.delete = to_streamed_response_wrapper( + shares.delete, + ) + + +class AsyncSharesResourceWithStreamingResponse: + def __init__(self, shares: AsyncSharesResource) -> None: + self._shares = shares + + self.create = async_to_streamed_response_wrapper( + shares.create, + ) + self.delete = async_to_streamed_response_wrapper( + shares.delete, + ) diff --git a/src/gitpod/types/__init__.py b/src/gitpod/types/__init__.py index 7306513..41613c2 100644 --- a/src/gitpod/types/__init__.py +++ b/src/gitpod/types/__init__.py @@ -20,6 +20,7 @@ SecretRef as SecretRef, FieldValue as FieldValue, UserStatus as UserStatus, + ResourceRole as ResourceRole, ResourceType as ResourceType, TaskMetadata as TaskMetadata, TaskExecution as TaskExecution, diff --git a/src/gitpod/types/groups/__init__.py b/src/gitpod/types/groups/__init__.py index abbe99f..384b0a7 100644 --- a/src/gitpod/types/groups/__init__.py +++ b/src/gitpod/types/groups/__init__.py @@ -2,9 +2,10 @@ from __future__ import annotations -from .resource_role import ResourceRole as ResourceRole from .role_assignment import RoleAssignment as RoleAssignment from .group_membership import GroupMembership as GroupMembership +from .share_create_params import ShareCreateParams as ShareCreateParams +from .share_delete_params import ShareDeleteParams as ShareDeleteParams from .membership_list_params import MembershipListParams as MembershipListParams from .membership_create_params import MembershipCreateParams as MembershipCreateParams from .membership_delete_params import MembershipDeleteParams as MembershipDeleteParams diff --git a/src/gitpod/types/groups/role_assignment.py b/src/gitpod/types/groups/role_assignment.py index 38e30ea..39512d0 100644 --- a/src/gitpod/types/groups/role_assignment.py +++ b/src/gitpod/types/groups/role_assignment.py @@ -5,7 +5,7 @@ from pydantic import Field as FieldInfo from ..._models import BaseModel -from .resource_role import ResourceRole +from ..shared.resource_role import ResourceRole from ..shared.resource_type import ResourceType __all__ = ["RoleAssignment"] diff --git a/src/gitpod/types/groups/role_assignment_create_params.py b/src/gitpod/types/groups/role_assignment_create_params.py index d036e80..deee188 100644 --- a/src/gitpod/types/groups/role_assignment_create_params.py +++ b/src/gitpod/types/groups/role_assignment_create_params.py @@ -5,7 +5,7 @@ from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo -from .resource_role import ResourceRole +from ..shared.resource_role import ResourceRole from ..shared.resource_type import ResourceType __all__ = ["RoleAssignmentCreateParams"] diff --git a/src/gitpod/types/groups/role_assignment_list_params.py b/src/gitpod/types/groups/role_assignment_list_params.py index 23c236b..63b97bd 100644 --- a/src/gitpod/types/groups/role_assignment_list_params.py +++ b/src/gitpod/types/groups/role_assignment_list_params.py @@ -6,7 +6,7 @@ from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo -from .resource_role import ResourceRole +from ..shared.resource_role import ResourceRole from ..shared.resource_type import ResourceType __all__ = ["RoleAssignmentListParams", "Filter", "Pagination"] diff --git a/src/gitpod/types/groups/share_create_params.py b/src/gitpod/types/groups/share_create_params.py new file mode 100644 index 0000000..3aba19d --- /dev/null +++ b/src/gitpod/types/groups/share_create_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo +from ..shared.principal import Principal +from ..shared.resource_role import ResourceRole +from ..shared.resource_type import ResourceType + +__all__ = ["ShareCreateParams"] + + +class ShareCreateParams(TypedDict, total=False): + principal: Principal + """Type of principal to share with (user or service account)""" + + principal_id: Annotated[str, PropertyInfo(alias="principalId")] + """ID of the principal (user or service account) to share with""" + + resource_id: Annotated[str, PropertyInfo(alias="resourceId")] + """ID of the resource to share""" + + resource_type: Annotated[ResourceType, PropertyInfo(alias="resourceType")] + """Type of resource to share (runner, project, etc.)""" + + role: ResourceRole + """Role to grant the principal on the resource""" diff --git a/src/gitpod/types/groups/share_delete_params.py b/src/gitpod/types/groups/share_delete_params.py new file mode 100644 index 0000000..e855a21 --- /dev/null +++ b/src/gitpod/types/groups/share_delete_params.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo +from ..shared.principal import Principal +from ..shared.resource_type import ResourceType + +__all__ = ["ShareDeleteParams"] + + +class ShareDeleteParams(TypedDict, total=False): + principal: Principal + """Type of principal to remove access from (user or service account)""" + + principal_id: Annotated[str, PropertyInfo(alias="principalId")] + """ID of the principal (user or service account) to remove access from""" + + resource_id: Annotated[str, PropertyInfo(alias="resourceId")] + """ID of the resource to unshare""" + + resource_type: Annotated[ResourceType, PropertyInfo(alias="resourceType")] + """Type of resource to unshare""" diff --git a/src/gitpod/types/shared/__init__.py b/src/gitpod/types/shared/__init__.py index de9766f..04e206d 100644 --- a/src/gitpod/types/shared/__init__.py +++ b/src/gitpod/types/shared/__init__.py @@ -11,6 +11,7 @@ from .secret_ref import SecretRef as SecretRef from .field_value import FieldValue as FieldValue from .user_status import UserStatus as UserStatus +from .resource_role import ResourceRole as ResourceRole from .resource_type import ResourceType as ResourceType from .task_metadata import TaskMetadata as TaskMetadata from .task_execution import TaskExecution as TaskExecution diff --git a/src/gitpod/types/groups/resource_role.py b/src/gitpod/types/shared/resource_role.py similarity index 100% rename from src/gitpod/types/groups/resource_role.py rename to src/gitpod/types/shared/resource_role.py diff --git a/src/gitpod/types/shared_params/__init__.py b/src/gitpod/types/shared_params/__init__.py index 093bf76..1d82ded 100644 --- a/src/gitpod/types/shared_params/__init__.py +++ b/src/gitpod/types/shared_params/__init__.py @@ -8,6 +8,7 @@ from .secret_ref import SecretRef as SecretRef from .field_value import FieldValue as FieldValue from .user_status import UserStatus as UserStatus +from .resource_role import ResourceRole as ResourceRole from .resource_type import ResourceType as ResourceType from .task_metadata import TaskMetadata as TaskMetadata from .environment_class import EnvironmentClass as EnvironmentClass diff --git a/src/gitpod/types/shared_params/resource_role.py b/src/gitpod/types/shared_params/resource_role.py new file mode 100644 index 0000000..053e954 --- /dev/null +++ b/src/gitpod/types/shared_params/resource_role.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ResourceRole"] + +ResourceRole: TypeAlias = Literal[ + "RESOURCE_ROLE_UNSPECIFIED", + "RESOURCE_ROLE_ORG_ADMIN", + "RESOURCE_ROLE_ORG_MEMBER", + "RESOURCE_ROLE_ORG_RUNNERS_ADMIN", + "RESOURCE_ROLE_GROUP_ADMIN", + "RESOURCE_ROLE_GROUP_VIEWER", + "RESOURCE_ROLE_USER_IDENTITY", + "RESOURCE_ROLE_USER_VIEWER", + "RESOURCE_ROLE_USER_ADMIN", + "RESOURCE_ROLE_ENVIRONMENT_IDENTITY", + "RESOURCE_ROLE_ENVIRONMENT_ADMIN", + "RESOURCE_ROLE_ENVIRONMENT_USER", + "RESOURCE_ROLE_ENVIRONMENT_VIEWER", + "RESOURCE_ROLE_ENVIRONMENT_RUNNER", + "RESOURCE_ROLE_RUNNER_IDENTITY", + "RESOURCE_ROLE_RUNNER_ADMIN", + "RESOURCE_ROLE_RUNNER_LOCAL_ADMIN", + "RESOURCE_ROLE_RUNNER_MANAGED_ADMIN", + "RESOURCE_ROLE_RUNNER_USER", + "RESOURCE_ROLE_RUNNER_CONFIGURATION_READER", + "RESOURCE_ROLE_HOST_AUTHENTICATION_TOKEN_ADMIN", + "RESOURCE_ROLE_HOST_AUTHENTICATION_TOKEN_UPDATER", + "RESOURCE_ROLE_PROJECT_ADMIN", + "RESOURCE_ROLE_PROJECT_USER", + "RESOURCE_ROLE_PROJECT_EDITOR", + "RESOURCE_ROLE_ENVIRONMENT_SERVICE_ADMIN", + "RESOURCE_ROLE_ENVIRONMENT_SERVICE_VIEWER", + "RESOURCE_ROLE_ENVIRONMENT_SERVICE_USER", + "RESOURCE_ROLE_ENVIRONMENT_SERVICE_ENV", + "RESOURCE_ROLE_ENVIRONMENT_TASK_ADMIN", + "RESOURCE_ROLE_ENVIRONMENT_TASK_VIEWER", + "RESOURCE_ROLE_ENVIRONMENT_TASK_USER", + "RESOURCE_ROLE_ENVIRONMENT_TASK_ENV", + "RESOURCE_ROLE_SERVICE_ACCOUNT_IDENTITY", + "RESOURCE_ROLE_SERVICE_ACCOUNT_ADMIN", + "RESOURCE_ROLE_AGENT_EXECUTION_IDENTITY", + "RESOURCE_ROLE_AGENT_EXECUTION_USER", + "RESOURCE_ROLE_AGENT_EXECUTION_ADMIN", + "RESOURCE_ROLE_AGENT_EXECUTION_RUNNER", + "RESOURCE_ROLE_AGENT_EXECUTION_OUTPUTS_REPORTER", + "RESOURCE_ROLE_AGENT_ADMIN", + "RESOURCE_ROLE_AGENT_VIEWER", + "RESOURCE_ROLE_AGENT_EXECUTOR", + "RESOURCE_ROLE_WORKFLOW_ADMIN", + "RESOURCE_ROLE_WORKFLOW_USER", + "RESOURCE_ROLE_WORKFLOW_VIEWER", + "RESOURCE_ROLE_WORKFLOW_EXECUTOR", + "RESOURCE_ROLE_SNAPSHOT_ADMIN", + "RESOURCE_ROLE_SNAPSHOT_RUNNER", + "RESOURCE_ROLE_WEBHOOK_ADMIN", + "RESOURCE_ROLE_WEBHOOK_VIEWER", +] diff --git a/tests/api_resources/groups/test_shares.py b/tests/api_resources/groups/test_shares.py new file mode 100644 index 0000000..3e0c57f --- /dev/null +++ b/tests/api_resources/groups/test_shares.py @@ -0,0 +1,181 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from gitpod import Gitpod, AsyncGitpod +from tests.utils import assert_matches_type + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestShares: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create(self, client: Gitpod) -> None: + share = client.groups.shares.create() + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Gitpod) -> None: + share = client.groups.shares.create( + principal="PRINCIPAL_SERVICE_ACCOUNT", + principal_id="a1b2c3d4-5678-90ab-cdef-1234567890ab", + resource_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + resource_type="RESOURCE_TYPE_RUNNER", + role="RESOURCE_ROLE_RUNNER_USER", + ) + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_create(self, client: Gitpod) -> None: + response = client.groups.shares.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + share = response.parse() + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_create(self, client: Gitpod) -> None: + with client.groups.shares.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + share = response.parse() + assert_matches_type(object, share, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete(self, client: Gitpod) -> None: + share = client.groups.shares.delete() + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_with_all_params(self, client: Gitpod) -> None: + share = client.groups.shares.delete( + principal="PRINCIPAL_USER", + principal_id="f53d2330-3795-4c5d-a1f3-453121af9c60", + resource_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + resource_type="RESOURCE_TYPE_RUNNER", + ) + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_delete(self, client: Gitpod) -> None: + response = client.groups.shares.with_raw_response.delete() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + share = response.parse() + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: Gitpod) -> None: + with client.groups.shares.with_streaming_response.delete() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + share = response.parse() + assert_matches_type(object, share, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncShares: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncGitpod) -> None: + share = await async_client.groups.shares.create() + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncGitpod) -> None: + share = await async_client.groups.shares.create( + principal="PRINCIPAL_SERVICE_ACCOUNT", + principal_id="a1b2c3d4-5678-90ab-cdef-1234567890ab", + resource_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + resource_type="RESOURCE_TYPE_RUNNER", + role="RESOURCE_ROLE_RUNNER_USER", + ) + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncGitpod) -> None: + response = await async_client.groups.shares.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + share = await response.parse() + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncGitpod) -> None: + async with async_client.groups.shares.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + share = await response.parse() + assert_matches_type(object, share, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncGitpod) -> None: + share = await async_client.groups.shares.delete() + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_with_all_params(self, async_client: AsyncGitpod) -> None: + share = await async_client.groups.shares.delete( + principal="PRINCIPAL_USER", + principal_id="f53d2330-3795-4c5d-a1f3-453121af9c60", + resource_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + resource_type="RESOURCE_TYPE_RUNNER", + ) + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncGitpod) -> None: + response = await async_client.groups.shares.with_raw_response.delete() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + share = await response.parse() + assert_matches_type(object, share, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncGitpod) -> None: + async with async_client.groups.shares.with_streaming_response.delete() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + share = await response.parse() + assert_matches_type(object, share, path=["response"]) + + assert cast(Any, response.is_closed) is True From b34ed1b8ecd39343ed02c5f376e9495775912140 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 08:59:29 +0000 Subject: [PATCH 22/27] feat(api): add CheckRepositoryAccess API for repository access validation --- .stats.yml | 8 +- api.md | 2 + src/gitpod/resources/runners/runners.py | 142 ++++++++++++++++++ src/gitpod/types/__init__.py | 6 + .../runner_check_repository_access_params.py | 19 +++ ...runner_check_repository_access_response.py | 20 +++ tests/api_resources/test_runners.py | 75 +++++++++ 7 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 src/gitpod/types/runner_check_repository_access_params.py create mode 100644 src/gitpod/types/runner_check_repository_access_response.py diff --git a/.stats.yml b/.stats.yml index 896d8f2..d450123 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 169 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-6b80aebab53bff73bb7b87c5f91f98c6dd5db2d623b6c613aaa5e61252b74d75.yml -openapi_spec_hash: 9636e315ac739c1ab9cba65a7ead6559 -config_hash: 73893621fd64bbd87b86671decf334e6 +configured_endpoints: 170 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-024993504dfc744ff138b0d1b1a151e28129f9f5a8b55adff4c7b559b0556c62.yml +openapi_spec_hash: 4773277eb2a6ac6be0b2e8f89a2201d8 +config_hash: 942ffc968ca0131e192c1a7ac5dd8990 diff --git a/api.md b/api.md index 796ab4b..0b70b7b 100644 --- a/api.md +++ b/api.md @@ -597,6 +597,7 @@ from gitpod.types import ( RunnerCreateResponse, RunnerRetrieveResponse, RunnerCheckAuthenticationForHostResponse, + RunnerCheckRepositoryAccessResponse, RunnerCreateLogsTokenResponse, RunnerCreateRunnerTokenResponse, RunnerListScmOrganizationsResponse, @@ -613,6 +614,7 @@ Methods: - client.runners.list(\*\*params) -> SyncRunnersPage[Runner] - client.runners.delete(\*\*params) -> object - client.runners.check_authentication_for_host(\*\*params) -> RunnerCheckAuthenticationForHostResponse +- client.runners.check_repository_access(\*\*params) -> RunnerCheckRepositoryAccessResponse - client.runners.create_logs_token(\*\*params) -> RunnerCreateLogsTokenResponse - client.runners.create_runner_token(\*\*params) -> RunnerCreateRunnerTokenResponse - client.runners.list_scm_organizations(\*\*params) -> RunnerListScmOrganizationsResponse diff --git a/src/gitpod/resources/runners/runners.py b/src/gitpod/resources/runners/runners.py index 0ba69c1..5a0ee2e 100644 --- a/src/gitpod/resources/runners/runners.py +++ b/src/gitpod/resources/runners/runners.py @@ -20,6 +20,7 @@ runner_create_runner_token_params, runner_search_repositories_params, runner_list_scm_organizations_params, + runner_check_repository_access_params, runner_check_authentication_for_host_params, ) from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given @@ -62,6 +63,7 @@ from ...types.runner_create_runner_token_response import RunnerCreateRunnerTokenResponse from ...types.runner_search_repositories_response import RunnerSearchRepositoriesResponse from ...types.runner_list_scm_organizations_response import RunnerListScmOrganizationsResponse +from ...types.runner_check_repository_access_response import RunnerCheckRepositoryAccessResponse from ...types.runner_check_authentication_for_host_response import RunnerCheckAuthenticationForHostResponse __all__ = ["RunnersResource", "AsyncRunnersResource"] @@ -510,6 +512,70 @@ def check_authentication_for_host( cast_to=RunnerCheckAuthenticationForHostResponse, ) + def check_repository_access( + self, + *, + repository_url: str | Omit = omit, + runner_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunnerCheckRepositoryAccessResponse: + """ + Checks if a principal has read access to a repository. + + Use this method to: + + - Validate repository access before workflow execution + - Verify executor credentials for automation bindings + + Returns: + + - has_access: true if the principal can read the repository + - FAILED_PRECONDITION if authentication is required + - INVALID_ARGUMENT if the repository URL is invalid + + ### Examples + + - Check access: + + Verifies read access to a repository. + + ```yaml + runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + repositoryUrl: "https://github.com/org/repo" + ``` + + Args: + repository_url: repository_url is the URL of the repository to check access for. Can be a clone + URL (https://github.com/org/repo.git) or web URL (https://github.com/org/repo). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/gitpod.v1.RunnerService/CheckRepositoryAccess", + body=maybe_transform( + { + "repository_url": repository_url, + "runner_id": runner_id, + }, + runner_check_repository_access_params.RunnerCheckRepositoryAccessParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunnerCheckRepositoryAccessResponse, + ) + def create_logs_token( self, *, @@ -1273,6 +1339,70 @@ async def check_authentication_for_host( cast_to=RunnerCheckAuthenticationForHostResponse, ) + async def check_repository_access( + self, + *, + repository_url: str | Omit = omit, + runner_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RunnerCheckRepositoryAccessResponse: + """ + Checks if a principal has read access to a repository. + + Use this method to: + + - Validate repository access before workflow execution + - Verify executor credentials for automation bindings + + Returns: + + - has_access: true if the principal can read the repository + - FAILED_PRECONDITION if authentication is required + - INVALID_ARGUMENT if the repository URL is invalid + + ### Examples + + - Check access: + + Verifies read access to a repository. + + ```yaml + runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68" + repositoryUrl: "https://github.com/org/repo" + ``` + + Args: + repository_url: repository_url is the URL of the repository to check access for. Can be a clone + URL (https://github.com/org/repo.git) or web URL (https://github.com/org/repo). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/gitpod.v1.RunnerService/CheckRepositoryAccess", + body=await async_maybe_transform( + { + "repository_url": repository_url, + "runner_id": runner_id, + }, + runner_check_repository_access_params.RunnerCheckRepositoryAccessParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=RunnerCheckRepositoryAccessResponse, + ) + async def create_logs_token( self, *, @@ -1617,6 +1747,9 @@ def __init__(self, runners: RunnersResource) -> None: self.check_authentication_for_host = to_raw_response_wrapper( runners.check_authentication_for_host, ) + self.check_repository_access = to_raw_response_wrapper( + runners.check_repository_access, + ) self.create_logs_token = to_raw_response_wrapper( runners.create_logs_token, ) @@ -1664,6 +1797,9 @@ def __init__(self, runners: AsyncRunnersResource) -> None: self.check_authentication_for_host = async_to_raw_response_wrapper( runners.check_authentication_for_host, ) + self.check_repository_access = async_to_raw_response_wrapper( + runners.check_repository_access, + ) self.create_logs_token = async_to_raw_response_wrapper( runners.create_logs_token, ) @@ -1711,6 +1847,9 @@ def __init__(self, runners: RunnersResource) -> None: self.check_authentication_for_host = to_streamed_response_wrapper( runners.check_authentication_for_host, ) + self.check_repository_access = to_streamed_response_wrapper( + runners.check_repository_access, + ) self.create_logs_token = to_streamed_response_wrapper( runners.create_logs_token, ) @@ -1758,6 +1897,9 @@ def __init__(self, runners: AsyncRunnersResource) -> None: self.check_authentication_for_host = async_to_streamed_response_wrapper( runners.check_authentication_for_host, ) + self.check_repository_access = async_to_streamed_response_wrapper( + runners.check_repository_access, + ) self.create_logs_token = async_to_streamed_response_wrapper( runners.create_logs_token, ) diff --git a/src/gitpod/types/__init__.py b/src/gitpod/types/__init__.py index 41613c2..4b195c7 100644 --- a/src/gitpod/types/__init__.py +++ b/src/gitpod/types/__init__.py @@ -224,6 +224,9 @@ from .project_prebuild_configuration_param import ProjectPrebuildConfigurationParam as ProjectPrebuildConfigurationParam from .runner_list_scm_organizations_params import RunnerListScmOrganizationsParams as RunnerListScmOrganizationsParams from .user_get_authenticated_user_response import UserGetAuthenticatedUserResponse as UserGetAuthenticatedUserResponse +from .runner_check_repository_access_params import ( + RunnerCheckRepositoryAccessParams as RunnerCheckRepositoryAccessParams, +) from .environment_create_from_project_params import ( EnvironmentCreateFromProjectParams as EnvironmentCreateFromProjectParams, ) @@ -236,6 +239,9 @@ from .runner_list_scm_organizations_response import ( RunnerListScmOrganizationsResponse as RunnerListScmOrganizationsResponse, ) +from .runner_check_repository_access_response import ( + RunnerCheckRepositoryAccessResponse as RunnerCheckRepositoryAccessResponse, +) from .environment_create_from_project_response import ( EnvironmentCreateFromProjectResponse as EnvironmentCreateFromProjectResponse, ) diff --git a/src/gitpod/types/runner_check_repository_access_params.py b/src/gitpod/types/runner_check_repository_access_params.py new file mode 100644 index 0000000..d118429 --- /dev/null +++ b/src/gitpod/types/runner_check_repository_access_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["RunnerCheckRepositoryAccessParams"] + + +class RunnerCheckRepositoryAccessParams(TypedDict, total=False): + repository_url: Annotated[str, PropertyInfo(alias="repositoryUrl")] + """ + repository_url is the URL of the repository to check access for. Can be a clone + URL (https://github.com/org/repo.git) or web URL (https://github.com/org/repo). + """ + + runner_id: Annotated[str, PropertyInfo(alias="runnerId")] diff --git a/src/gitpod/types/runner_check_repository_access_response.py b/src/gitpod/types/runner_check_repository_access_response.py new file mode 100644 index 0000000..11b7536 --- /dev/null +++ b/src/gitpod/types/runner_check_repository_access_response.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["RunnerCheckRepositoryAccessResponse"] + + +class RunnerCheckRepositoryAccessResponse(BaseModel): + error_message: Optional[str] = FieldInfo(alias="errorMessage", default=None) + """ + error_message provides details when access check fails. Empty when has_access is + true. + """ + + has_access: Optional[bool] = FieldInfo(alias="hasAccess", default=None) + """has_access indicates whether the principal has read access to the repository.""" diff --git a/tests/api_resources/test_runners.py b/tests/api_resources/test_runners.py index 5cf72fe..eaf96b9 100644 --- a/tests/api_resources/test_runners.py +++ b/tests/api_resources/test_runners.py @@ -18,6 +18,7 @@ RunnerCreateRunnerTokenResponse, RunnerSearchRepositoriesResponse, RunnerListScmOrganizationsResponse, + RunnerCheckRepositoryAccessResponse, RunnerCheckAuthenticationForHostResponse, ) from gitpod.pagination import SyncRunnersPage, AsyncRunnersPage @@ -292,6 +293,43 @@ def test_streaming_response_check_authentication_for_host(self, client: Gitpod) assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_check_repository_access(self, client: Gitpod) -> None: + runner = client.runners.check_repository_access() + assert_matches_type(RunnerCheckRepositoryAccessResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_check_repository_access_with_all_params(self, client: Gitpod) -> None: + runner = client.runners.check_repository_access( + repository_url="https://github.com/org/repo", + runner_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(RunnerCheckRepositoryAccessResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_check_repository_access(self, client: Gitpod) -> None: + response = client.runners.with_raw_response.check_repository_access() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + runner = response.parse() + assert_matches_type(RunnerCheckRepositoryAccessResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_check_repository_access(self, client: Gitpod) -> None: + with client.runners.with_streaming_response.check_repository_access() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + runner = response.parse() + assert_matches_type(RunnerCheckRepositoryAccessResponse, runner, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_logs_token(self, client: Gitpod) -> None: @@ -754,6 +792,43 @@ async def test_streaming_response_check_authentication_for_host(self, async_clie assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_check_repository_access(self, async_client: AsyncGitpod) -> None: + runner = await async_client.runners.check_repository_access() + assert_matches_type(RunnerCheckRepositoryAccessResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_check_repository_access_with_all_params(self, async_client: AsyncGitpod) -> None: + runner = await async_client.runners.check_repository_access( + repository_url="https://github.com/org/repo", + runner_id="d2c94c27-3b76-4a42-b88c-95a85e392c68", + ) + assert_matches_type(RunnerCheckRepositoryAccessResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_check_repository_access(self, async_client: AsyncGitpod) -> None: + response = await async_client.runners.with_raw_response.check_repository_access() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + runner = await response.parse() + assert_matches_type(RunnerCheckRepositoryAccessResponse, runner, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_check_repository_access(self, async_client: AsyncGitpod) -> None: + async with async_client.runners.with_streaming_response.check_repository_access() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + runner = await response.parse() + assert_matches_type(RunnerCheckRepositoryAccessResponse, runner, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_logs_token(self, async_client: AsyncGitpod) -> None: From 1c670ce07e8c40ce649b758fe70374c7a5ead16e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 09:21:58 +0000 Subject: [PATCH 23/27] codegen metadata --- .stats.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index d450123..718b910 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 170 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-024993504dfc744ff138b0d1b1a151e28129f9f5a8b55adff4c7b559b0556c62.yml -openapi_spec_hash: 4773277eb2a6ac6be0b2e8f89a2201d8 -config_hash: 942ffc968ca0131e192c1a7ac5dd8990 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-eeec67988ceb123d93514d894e04b0fa00dbf0c4678b95baa80868c103aaa0c1.yml +openapi_spec_hash: 3bf178a4e70c15f12fdc23531fe81aea +config_hash: d726afb2a92132197e4eae04303e8041 From 8262825c21be1de663b9a3aad29e9f9fe1cf219d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 13:28:15 +0000 Subject: [PATCH 24/27] feat(api): add inputs array to UserInputBlock proto --- .stats.yml | 4 +- src/gitpod/types/user_input_block_param.py | 47 ++++++++++++++++++---- tests/api_resources/test_agents.py | 28 ++++++++++++- 3 files changed, 68 insertions(+), 11 deletions(-) diff --git a/.stats.yml b/.stats.yml index 718b910..de7a963 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 170 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-eeec67988ceb123d93514d894e04b0fa00dbf0c4678b95baa80868c103aaa0c1.yml -openapi_spec_hash: 3bf178a4e70c15f12fdc23531fe81aea +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-f838b3c6096ed0143c969fcdd6acf63cb619865ef707e1213194d3b82afe10bf.yml +openapi_spec_hash: 00a77c0d2749b833f8e29c8493b7ea98 config_hash: d726afb2a92132197e4eae04303e8041 diff --git a/src/gitpod/types/user_input_block_param.py b/src/gitpod/types/user_input_block_param.py index 5a6b87e..905b928 100644 --- a/src/gitpod/types/user_input_block_param.py +++ b/src/gitpod/types/user_input_block_param.py @@ -2,30 +2,61 @@ from __future__ import annotations -from typing import Union +from typing import Union, Iterable from datetime import datetime -from typing_extensions import Annotated, TypedDict +from typing_extensions import Literal, Annotated, TypedDict from .._types import Base64FileInput from .._utils import PropertyInfo from .._models import set_pydantic_config -__all__ = ["UserInputBlockParam", "Image", "Text"] +__all__ = ["UserInputBlockParam", "Image", "Input", "InputImage", "InputText", "Text"] class Image(TypedDict, total=False): """ ImageInput allows sending images to the agent. - Media type is inferred from magic bytes by the backend. + Client must provide the MIME type; backend validates against magic bytes. """ data: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] - """Raw image data (max 4MB). Supported formats: PNG, JPEG, WebP.""" + """Raw image data (max 4MB). Supported formats: PNG, JPEG.""" + + mime_type: Annotated[Literal["image/png", "image/jpeg"], PropertyInfo(alias="mimeType")] set_pydantic_config(Image, {"arbitrary_types_allowed": True}) +class InputImage(TypedDict, total=False): + """ + ImageInput allows sending images to the agent. + Client must provide the MIME type; backend validates against magic bytes. + """ + + data: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] + """Raw image data (max 4MB). Supported formats: PNG, JPEG.""" + + mime_type: Annotated[Literal["image/png", "image/jpeg"], PropertyInfo(alias="mimeType")] + + +set_pydantic_config(InputImage, {"arbitrary_types_allowed": True}) + + +class InputText(TypedDict, total=False): + content: str + + +class Input(TypedDict, total=False): + image: InputImage + """ + ImageInput allows sending images to the agent. Client must provide the MIME + type; backend validates against magic bytes. + """ + + text: InputText + + class Text(TypedDict, total=False): content: str @@ -38,8 +69,10 @@ class UserInputBlockParam(TypedDict, total=False): image: Image """ - ImageInput allows sending images to the agent. Media type is inferred from magic - bytes by the backend. + ImageInput allows sending images to the agent. Client must provide the MIME + type; backend validates against magic bytes. """ + inputs: Iterable[Input] + text: Text diff --git a/tests/api_resources/test_agents.py b/tests/api_resources/test_agents.py index f4f2aad..e006c3c 100644 --- a/tests/api_resources/test_agents.py +++ b/tests/api_resources/test_agents.py @@ -361,7 +361,19 @@ def test_method_send_to_execution_with_all_params(self, client: Gitpod) -> None: user_input={ "id": "id", "created_at": parse_datetime("2019-12-27T18:11:19.117Z"), - "image": {"data": "U3RhaW5sZXNzIHJvY2tz"}, + "image": { + "data": "U3RhaW5sZXNzIHJvY2tz", + "mime_type": "image/png", + }, + "inputs": [ + { + "image": { + "data": "U3RhaW5sZXNzIHJvY2tz", + "mime_type": "image/png", + }, + "text": {"content": "x"}, + } + ], "text": {"content": "Generate a report based on the latest logs."}, }, ) @@ -874,7 +886,19 @@ async def test_method_send_to_execution_with_all_params(self, async_client: Asyn user_input={ "id": "id", "created_at": parse_datetime("2019-12-27T18:11:19.117Z"), - "image": {"data": "U3RhaW5sZXNzIHJvY2tz"}, + "image": { + "data": "U3RhaW5sZXNzIHJvY2tz", + "mime_type": "image/png", + }, + "inputs": [ + { + "image": { + "data": "U3RhaW5sZXNzIHJvY2tz", + "mime_type": "image/png", + }, + "text": {"content": "x"}, + } + ], "text": {"content": "Generate a report based on the latest logs."}, }, ) From de6bee5d7d337456f1a19de0659cd6957c7c1a9b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 13:43:59 +0000 Subject: [PATCH 25/27] feat(agent): add spec mode for planning before interactive implementation --- .stats.yml | 4 ++-- src/gitpod/types/agent_mode.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index de7a963..bec4a22 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 170 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-f838b3c6096ed0143c969fcdd6acf63cb619865ef707e1213194d3b82afe10bf.yml -openapi_spec_hash: 00a77c0d2749b833f8e29c8493b7ea98 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-df34fde966f11e17c7e733280ca1e0aeda7ee786ca7de6d245d649970fa71764.yml +openapi_spec_hash: 9c8c2465318c2d732de64a455cbd7704 config_hash: d726afb2a92132197e4eae04303e8041 diff --git a/src/gitpod/types/agent_mode.py b/src/gitpod/types/agent_mode.py index d7bd23b..a58c379 100644 --- a/src/gitpod/types/agent_mode.py +++ b/src/gitpod/types/agent_mode.py @@ -5,5 +5,5 @@ __all__ = ["AgentMode"] AgentMode: TypeAlias = Literal[ - "AGENT_MODE_UNSPECIFIED", "AGENT_MODE_EXECUTION", "AGENT_MODE_PLANNING", "AGENT_MODE_RALPH" + "AGENT_MODE_UNSPECIFIED", "AGENT_MODE_EXECUTION", "AGENT_MODE_PLANNING", "AGENT_MODE_RALPH", "AGENT_MODE_SPEC" ] From bf84d63dbf07f7a6bd5fa51cfa7bec30686ee0a6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 13:58:17 +0000 Subject: [PATCH 26/27] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index bec4a22..886c83c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 170 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-df34fde966f11e17c7e733280ca1e0aeda7ee786ca7de6d245d649970fa71764.yml -openapi_spec_hash: 9c8c2465318c2d732de64a455cbd7704 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-2423c089f280cdf34a987d40531692097a69f4aa971c6adf9aeec4fd7984cec2.yml +openapi_spec_hash: 24037c3ab9ceca689150d07ecec7aa80 config_hash: d726afb2a92132197e4eae04303e8041 From 4073835b00a55b7fe5bcca810116986ab25cc5f1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:29:16 +0000 Subject: [PATCH 27/27] release: 0.7.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/gitpod/_version.py | 2 +- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4208b5c..1b77f50 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.6.0" + ".": "0.7.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e6b615d..9d5779c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## 0.7.0 (2026-01-21) + +Full Changelog: [v0.6.0...v0.7.0](https://github.com/gitpod-io/gitpod-sdk-python/compare/v0.6.0...v0.7.0) + +### Features + +* [api] Introduce RPCs to share resources with individual users ([df1cf39](https://github.com/gitpod-io/gitpod-sdk-python/commit/df1cf39a9c1b387c9e9313bfec68a97759762e0b)) +* [api] sorting for `ListMembers` ([838e74c](https://github.com/gitpod-io/gitpod-sdk-python/commit/838e74c4da4b57590a6dd0af19bdd20faf7d2805)) +* [backend] Adding direct_share field to groups ([78c0bdd](https://github.com/gitpod-io/gitpod-sdk-python/commit/78c0bddc838e217007729d723283f9e9cd04d9a2)) +* [backend] Introduce role and member status filtering for `ListMembers` ([34fb372](https://github.com/gitpod-io/gitpod-sdk-python/commit/34fb372aef655ae57fc99d5b37e152c75d831af5)) +* **agent:** add spec mode for planning before interactive implementation ([de6bee5](https://github.com/gitpod-io/gitpod-sdk-python/commit/de6bee5d7d337456f1a19de0659cd6957c7c1a9b)) +* API for SCIM configuration management ([70becd4](https://github.com/gitpod-io/gitpod-sdk-python/commit/70becd4cd142fac4fc839018d52fb4cb93e17834)) +* **api:** add CheckRepositoryAccess API for repository access validation ([b34ed1b](https://github.com/gitpod-io/gitpod-sdk-python/commit/b34ed1b8ecd39343ed02c5f376e9495775912140)) +* **api:** add draft and state fields to PullRequest proto ([e0023da](https://github.com/gitpod-io/gitpod-sdk-python/commit/e0023da5a30344c2fc87ebce55e26101c4ad40b5)) +* **api:** add inputs array to UserInputBlock proto ([8262825](https://github.com/gitpod-io/gitpod-sdk-python/commit/8262825c21be1de663b9a3aad29e9f9fe1cf219d)) +* **api:** add ListSCMOrganizations endpoint ([9c8f7ea](https://github.com/gitpod-io/gitpod-sdk-python/commit/9c8f7eadd38bc0326ecf1be48706003fa258257c)) +* **api:** add search, creator, and status filters to ListWorkflows ([ddd18c0](https://github.com/gitpod-io/gitpod-sdk-python/commit/ddd18c09beb0f24e076818783d2dae09ca9b9f8b)) +* **api:** improve SearchRepositories pagination with next_page and total_count ([2847a10](https://github.com/gitpod-io/gitpod-sdk-python/commit/2847a10e6cbb09be83b012e8a6fcabd32f49e019)) +* **automations:** add before_snapshot trigger type ([9cd272f](https://github.com/gitpod-io/gitpod-sdk-python/commit/9cd272f98f2215b834a841ca34c52ce04fd2898e)) +* **client:** add support for binary request streaming ([be5a823](https://github.com/gitpod-io/gitpod-sdk-python/commit/be5a8235224ff1ecf25464e716191fbf3c7c7fb1)) +* **dashboard:** show tier badge in org selector ([89fd8fe](https://github.com/gitpod-io/gitpod-sdk-python/commit/89fd8fef7f9de200e4aecd563c965d4209427052)) +* Define SCIMConfiguration database schema ([03bd185](https://github.com/gitpod-io/gitpod-sdk-python/commit/03bd1858ec2aefbd4c20a71c206135c441afa99c)) +* move agent mode from Spec to Status, add AgentModeChange signals ([a55115b](https://github.com/gitpod-io/gitpod-sdk-python/commit/a55115ba054078dcb689222cc150b2b1f56077bf)) +* **secrets:** add ServiceAccountSecret entity with full support ([30e17c5](https://github.com/gitpod-io/gitpod-sdk-python/commit/30e17c55b991286527f64c8857b04dd9b5a2ba7b)) + + +### Chores + +* **internal:** update `actions/checkout` version ([53dcf30](https://github.com/gitpod-io/gitpod-sdk-python/commit/53dcf30cb41a6cbf30ce510b0b2d46cdd5895008)) + ## 0.6.0 (2026-01-09) Full Changelog: [v0.5.2...v0.6.0](https://github.com/gitpod-io/gitpod-sdk-python/compare/v0.5.2...v0.6.0) diff --git a/pyproject.toml b/pyproject.toml index 6136eaf..4f4d826 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "gitpod-sdk" -version = "0.6.0" +version = "0.7.0" description = "The official Python library for the gitpod API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/gitpod/_version.py b/src/gitpod/_version.py index 850eb95..88739dd 100644 --- a/src/gitpod/_version.py +++ b/src/gitpod/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "gitpod" -__version__ = "0.6.0" # x-release-please-version +__version__ = "0.7.0" # x-release-please-version