diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cc51f6f8e..fc0d7ff8b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.44.0" + ".": "0.45.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index a8ec4169e..daa2a1295 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 657 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-8c3bd3792724737d30da0f0200f0c589f9942ea3a028fcc9bcd56f3490ffc29a.yml -openapi_spec_hash: 7b184f4e52554b89cadf11b43f395583 -config_hash: aa5a9dd05b6324fcb454d0694e5901a3 +configured_endpoints: 658 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore/gcore-63b63d8be16530f04e4c07443b8f3ed2554e65dd6343d1ac13a48f89c2cf9fc4.yml +openapi_spec_hash: 6f72f97fd9545f3ecda586de840c68ee +config_hash: 88e4af508ede520a45a0563d9cf077cc diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c917e1bf..1618ea7e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # Changelog +## 0.45.0 (2026-05-04) + +Full Changelog: [v0.44.0...v0.45.0](https://github.com/G-Core/gcore-python/compare/v0.44.0...v0.45.0) + +### ⚠ BREAKING CHANGES + +* **cloud:** switch routers update from v1 to v2 +* **cloud:** correct pg conf validation response model name + +### Features + +* **api:** aggregated API specs update ([ee5d831](https://github.com/G-Core/gcore-python/commit/ee5d831570b46fb37395a5972d7e29112a7e6442)) +* **api:** aggregated API specs update ([98209fa](https://github.com/G-Core/gcore-python/commit/98209fad538c45d924d9ac35d058d22b3970069e)) +* **api:** aggregated API specs update ([377df41](https://github.com/G-Core/gcore-python/commit/377df417b485d59c6c58f3ccbd5155fadd2067f6)) +* **api:** aggregated API specs update ([8985183](https://github.com/G-Core/gcore-python/commit/898518310e07028a47f72a33f3524577f9e83376)) +* **api:** aggregated API specs update ([680e40d](https://github.com/G-Core/gcore-python/commit/680e40d2e2bc16202d0c70164a7f7603a133cd9c)) +* **cloud:** add update_and_poll method for routers ([6a3523a](https://github.com/G-Core/gcore-python/commit/6a3523a2eafdc436f176c5ea78f229331cc7f835)) +* **cloud:** switch routers update from v1 to v2 ([a4499c7](https://github.com/G-Core/gcore-python/commit/a4499c76aa8bf0b19792134c68bdb24d02732c61)) +* **cloud:** use routers update_and_poll in network examples ([f3e9aa6](https://github.com/G-Core/gcore-python/commit/f3e9aa6f969960316ac8282b2cb6875023bb17d8)) +* **iam:** migrate api_tokens to v2 endpoints ([fad3629](https://github.com/G-Core/gcore-python/commit/fad362941ce456930120fb2f8c22413807597d78)) +* **storage:** add get method for access keys ([b7a6c07](https://github.com/G-Core/gcore-python/commit/b7a6c074732285b2244eb012c6deef4bf39ad56e)) +* support setting headers via env ([48f6c89](https://github.com/G-Core/gcore-python/commit/48f6c89a1755f00ee53c9764273e937864c59666)) + + +### Bug Fixes + +* **cloud:** correct pg conf validation response model name ([c5bc635](https://github.com/G-Core/gcore-python/commit/c5bc635407def6e004bdb94a24e9045a1f8cf416)) +* **cloud:** match hw_machine_type literal in instances images poll methods ([1c3322d](https://github.com/G-Core/gcore-python/commit/1c3322d6535eb206742fce884d8ee9d51363c0dd)) +* use correct field name format for multipart file arrays ([5db7792](https://github.com/G-Core/gcore-python/commit/5db779213e0c088d2a615dccd43e0f7ca94bea1a)) + + +### Chores + +* **internal:** reformat pyproject.toml ([4701fe6](https://github.com/G-Core/gcore-python/commit/4701fe6e238ea59aec4b3de804e882e249e496be)) + ## 0.44.0 (2026-04-27) Full Changelog: [v0.43.0...v0.44.0](https://github.com/G-Core/gcore-python/compare/v0.43.0...v0.44.0) diff --git a/examples/cloud/networks.py b/examples/cloud/networks.py index 0ed195284..cca6c060d 100644 --- a/examples/cloud/networks.py +++ b/examples/cloud/networks.py @@ -136,7 +136,7 @@ def get_router(*, client: Gcore, router_id: str) -> None: def update_router(*, client: Gcore, router_id: str) -> None: print("\n=== UPDATE ROUTER ===") - router = client.cloud.networks.routers.update(router_id=router_id, name="gcore-go-example-updated") # pyright: ignore[reportDeprecated] + router = client.cloud.networks.routers.update_and_poll(router_id=router_id, name="gcore-go-example-updated") print(f"Updated router: ID={router.id}, name={router.name}") print("========================") diff --git a/examples/cloud/networks_async.py b/examples/cloud/networks_async.py index 8d76973b8..7683863a9 100644 --- a/examples/cloud/networks_async.py +++ b/examples/cloud/networks_async.py @@ -144,7 +144,7 @@ async def get_router(*, client: AsyncGcore, router_id: str) -> None: async def update_router(*, client: AsyncGcore, router_id: str) -> None: print("\n=== UPDATE ROUTER ===") - router = await client.cloud.networks.routers.update(router_id=router_id, name="gcore-go-example-updated") # pyright: ignore[reportDeprecated] + router = await client.cloud.networks.routers.update_and_poll(router_id=router_id, name="gcore-go-example-updated") print(f"Updated router: ID={router.id}, name={router.name}") print("========================") diff --git a/pyproject.toml b/pyproject.toml index 92c24b561..3cb70b034 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "gcore" -version = "0.44.0" +version = "0.45.0" description = "The official Python library for the gcore API" dynamic = ["readme"] license = "Apache-2.0" @@ -170,7 +170,7 @@ show_error_codes = true # # We also exclude our `tests` as mypy doesn't always infer # types correctly and Pyright will still catch any type errors. -exclude = ['src/gcore/_files.py', '_dev/.*.py', 'tests/.*'] +exclude = ["src/gcore/_files.py", "_dev/.*.py", "tests/.*"] strict_equality = true implicit_reexport = true diff --git a/src/gcore/_client.py b/src/gcore/_client.py index ad06f5f02..de48ead3b 100644 --- a/src/gcore/_client.py +++ b/src/gcore/_client.py @@ -19,7 +19,12 @@ RequestOptions, not_given, ) -from ._utils import is_given, get_async_library, maybe_coerce_integer +from ._utils import ( + is_given, + is_mapping_t, + get_async_library, + maybe_coerce_integer, +) from ._compat import cached_property from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream @@ -116,6 +121,15 @@ def __init__( if base_url is None: base_url = f"https://api.gcore.com" + custom_headers_env = os.environ.get("GCORE_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, @@ -395,6 +409,15 @@ def __init__( if base_url is None: base_url = f"https://api.gcore.com" + custom_headers_env = os.environ.get("GCORE_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, diff --git a/src/gcore/_qs.py b/src/gcore/_qs.py index de8c99bc6..4127c19c6 100644 --- a/src/gcore/_qs.py +++ b/src/gcore/_qs.py @@ -2,17 +2,13 @@ from typing import Any, List, Tuple, Union, Mapping, TypeVar from urllib.parse import parse_qs, urlencode -from typing_extensions import Literal, get_args +from typing_extensions import get_args -from ._types import NotGiven, not_given +from ._types import NotGiven, ArrayFormat, NestedFormat, not_given from ._utils import flatten _T = TypeVar("_T") - -ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] -NestedFormat = Literal["dots", "brackets"] - PrimitiveData = Union[str, int, float, bool, None] # this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] # https://github.com/microsoft/pyright/issues/3555 diff --git a/src/gcore/_types.py b/src/gcore/_types.py index bbc3b7c8f..8a2122e52 100644 --- a/src/gcore/_types.py +++ b/src/gcore/_types.py @@ -47,6 +47,9 @@ ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) _T = TypeVar("_T") +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + # Approximates httpx internal ProxiesTypes and RequestFiles types # while adding support for `PathLike` instances diff --git a/src/gcore/_utils/_utils.py b/src/gcore/_utils/_utils.py index 771859f5e..199cd231f 100644 --- a/src/gcore/_utils/_utils.py +++ b/src/gcore/_utils/_utils.py @@ -17,11 +17,11 @@ ) from pathlib import Path from datetime import date, datetime -from typing_extensions import TypeGuard +from typing_extensions import TypeGuard, get_args import sniffio -from .._types import Omit, NotGiven, FileTypes, HeadersLike +from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -40,25 +40,45 @@ def extract_files( query: Mapping[str, object], *, paths: Sequence[Sequence[str]], + array_format: ArrayFormat = "brackets", ) -> list[tuple[str, FileTypes]]: """Recursively extract files from the given dictionary based on specified paths. A path may look like this ['foo', 'files', '', 'data']. + ``array_format`` controls how ```` segments contribute to the emitted + field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and + ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``). + Note: this mutates the given dictionary. """ files: list[tuple[str, FileTypes]] = [] for path in paths: - files.extend(_extract_items(query, path, index=0, flattened_key=None)) + files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format)) return files +def _array_suffix(array_format: ArrayFormat, array_index: int) -> str: + if array_format == "brackets": + return "[]" + if array_format == "indices": + return f"[{array_index}]" + if array_format == "repeat" or array_format == "comma": + # Both repeat the bare field name for each file part; there is no + # meaningful way to comma-join binary parts. + return "" + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + def _extract_items( obj: object, path: Sequence[str], *, index: int, flattened_key: str | None, + array_format: ArrayFormat, ) -> list[tuple[str, FileTypes]]: try: key = path[index] @@ -75,9 +95,11 @@ def _extract_items( if is_list(obj): files: list[tuple[str, FileTypes]] = [] - for entry in obj: - assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") - files.append((flattened_key + "[]", cast(FileTypes, entry))) + for array_index, entry in enumerate(obj): + suffix = _array_suffix(array_format, array_index) + emitted_key = (flattened_key + suffix) if flattened_key else suffix + assert_is_file_content(entry, key=emitted_key) + files.append((emitted_key, cast(FileTypes, entry))) return files assert_is_file_content(obj, key=flattened_key) @@ -106,6 +128,7 @@ def _extract_items( path, index=index, flattened_key=flattened_key, + array_format=array_format, ) elif is_list(obj): if key != "": @@ -117,9 +140,12 @@ def _extract_items( item, path, index=index, - flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + flattened_key=( + (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index) + ), + array_format=array_format, ) - for item in obj + for array_index, item in enumerate(obj) ] ) diff --git a/src/gcore/_version.py b/src/gcore/_version.py index 43e6ba1de..a2c340f23 100644 --- a/src/gcore/_version.py +++ b/src/gcore/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "gcore" -__version__ = "0.44.0" # x-release-please-version +__version__ = "0.45.0" # x-release-please-version diff --git a/src/gcore/resources/cloud/api.md b/src/gcore/resources/cloud/api.md index 2f1e734b0..710249015 100644 --- a/src/gcore/resources/cloud/api.md +++ b/src/gcore/resources/cloud/api.md @@ -387,7 +387,7 @@ from gcore.types.cloud.networks import Router, RouterList, SubnetID Methods: - client.cloud.networks.routers.create(\*, project_id, region_id, \*\*params) -> TaskIDList -- client.cloud.networks.routers.update(router_id, \*, project_id, region_id, \*\*params) -> Router +- client.cloud.networks.routers.update(router_id, \*, project_id, region_id, \*\*params) -> TaskIDList - client.cloud.networks.routers.list(\*, project_id, region_id, \*\*params) -> SyncOffsetPage[Router] - client.cloud.networks.routers.delete(router_id, \*, project_id, region_id) -> TaskIDList - client.cloud.networks.routers.attach_subnet(router_id, \*, project_id, region_id, \*\*params) -> Router @@ -1190,12 +1190,12 @@ Methods: Types: ```python -from gcore.types.cloud.databases.postgres import CustomConfigurationValidateResponse +from gcore.types.cloud.databases.postgres import PgConfValidation ``` Methods: -- client.cloud.databases.postgres.custom_configurations.validate(\*, project_id, region_id, \*\*params) -> CustomConfigurationValidateResponse +- client.cloud.databases.postgres.custom_configurations.validate(\*, project_id, region_id, \*\*params) -> PgConfValidation ## VolumeSnapshots diff --git a/src/gcore/resources/cloud/audit_logs.py b/src/gcore/resources/cloud/audit_logs.py index bca05f963..f8a1575a7 100644 --- a/src/gcore/resources/cloud/audit_logs.py +++ b/src/gcore/resources/cloud/audit_logs.py @@ -73,6 +73,7 @@ def list( "rebuild", "regenerate_credentials", "remove_from_servergroup", + "replace", "replace_metadata", "resize", "resume", @@ -291,6 +292,7 @@ def list( "rebuild", "regenerate_credentials", "remove_from_servergroup", + "replace", "replace_metadata", "resize", "resume", diff --git a/src/gcore/resources/cloud/databases/postgres/custom_configurations.py b/src/gcore/resources/cloud/databases/postgres/custom_configurations.py index c4701fe80..9e8a3e2bd 100644 --- a/src/gcore/resources/cloud/databases/postgres/custom_configurations.py +++ b/src/gcore/resources/cloud/databases/postgres/custom_configurations.py @@ -16,9 +16,7 @@ ) from ....._base_client import make_request_options from .....types.cloud.databases.postgres import custom_configuration_validate_params -from .....types.cloud.databases.postgres.custom_configuration_validate_response import ( - CustomConfigurationValidateResponse, -) +from .....types.cloud.databases.postgres.pg_conf_validation import PgConfValidation __all__ = ["CustomConfigurationsResource", "AsyncCustomConfigurationsResource"] @@ -56,7 +54,7 @@ def validate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> CustomConfigurationValidateResponse: + ) -> PgConfValidation: """ Validate a custom PostgreSQL configuration file. @@ -97,7 +95,7 @@ def validate( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=CustomConfigurationValidateResponse, + cast_to=PgConfValidation, ) @@ -134,7 +132,7 @@ async def validate( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> CustomConfigurationValidateResponse: + ) -> PgConfValidation: """ Validate a custom PostgreSQL configuration file. @@ -175,7 +173,7 @@ async def validate( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=CustomConfigurationValidateResponse, + cast_to=PgConfValidation, ) diff --git a/src/gcore/resources/cloud/instances/images.py b/src/gcore/resources/cloud/instances/images.py index 97d689cf3..534a140e6 100644 --- a/src/gcore/resources/cloud/instances/images.py +++ b/src/gcore/resources/cloud/instances/images.py @@ -64,8 +64,8 @@ def update( project_id: int | None = None, region_id: int | None = None, hw_firmware_type: Literal["bios", "uefi"] | Omit = omit, - hw_machine_type: Literal["pc", "q35"] | Omit = omit, - is_baremetal: bool | Omit = omit, + hw_machine_type: Literal["i440", "q35"] | Omit = omit, + is_baremetal: Optional[bool] | Omit = omit, name: str | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, ssh_key: Literal["allow", "deny", "required"] | Omit = omit, @@ -81,6 +81,12 @@ def update( Update image properties and tags. Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + hw_firmware_type: Specifies the type of firmware with which to boot the guest. hw_machine_type: A virtual chipset type. @@ -229,6 +235,12 @@ def delete( snapshots. Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -301,7 +313,7 @@ def create_from_volume( volume_id: str, architecture: Literal["aarch64", "x86_64"] | Omit = omit, hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, - hw_machine_type: Optional[Literal["pc", "q35"]] | Omit = omit, + hw_machine_type: Optional[Literal["i440", "q35"]] | Omit = omit, is_baremetal: bool | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, source: Literal["volume"] | Omit = omit, @@ -393,7 +405,7 @@ def create_from_volume_and_poll( volume_id: str, architecture: Literal["aarch64", "x86_64"] | Omit = omit, hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, - hw_machine_type: Optional[Literal["pc", "q35"]] | Omit = omit, + hw_machine_type: Optional[Literal["i440", "q35"]] | Omit = omit, is_baremetal: bool | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, source: Literal["volume"] | Omit = omit, @@ -464,6 +476,12 @@ def get( Retrieve detailed information about a specific image. Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + include_prices: Show price extra_headers: Send extra headers @@ -507,7 +525,7 @@ def upload( architecture: Literal["aarch64", "x86_64"] | Omit = omit, cow_format: bool | Omit = omit, hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, - hw_machine_type: Optional[Literal["pc", "q35"]] | Omit = omit, + hw_machine_type: Optional[Literal["i440", "q35"]] | Omit = omit, is_baremetal: bool | Omit = omit, os_distro: Optional[str] | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, @@ -527,6 +545,10 @@ def upload( like OS type, architecture, and tags. Args: + project_id: Project ID + + region_id: Region ID + name: Image name url: URL @@ -606,7 +628,7 @@ def upload_and_poll( architecture: Literal["aarch64", "x86_64"] | Omit = omit, cow_format: bool | Omit = omit, hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, - hw_machine_type: Optional[Literal["pc", "q35"]] | Omit = omit, + hw_machine_type: Optional[Literal["i440", "q35"]] | Omit = omit, is_baremetal: bool | Omit = omit, os_distro: Optional[str] | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, @@ -694,8 +716,8 @@ async def update( project_id: int | None = None, region_id: int | None = None, hw_firmware_type: Literal["bios", "uefi"] | Omit = omit, - hw_machine_type: Literal["pc", "q35"] | Omit = omit, - is_baremetal: bool | Omit = omit, + hw_machine_type: Literal["i440", "q35"] | Omit = omit, + is_baremetal: Optional[bool] | Omit = omit, name: str | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, ssh_key: Literal["allow", "deny", "required"] | Omit = omit, @@ -711,6 +733,12 @@ async def update( Update image properties and tags. Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + hw_firmware_type: Specifies the type of firmware with which to boot the guest. hw_machine_type: A virtual chipset type. @@ -859,6 +887,12 @@ async def delete( snapshots. Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -931,7 +965,7 @@ async def create_from_volume( volume_id: str, architecture: Literal["aarch64", "x86_64"] | Omit = omit, hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, - hw_machine_type: Optional[Literal["pc", "q35"]] | Omit = omit, + hw_machine_type: Optional[Literal["i440", "q35"]] | Omit = omit, is_baremetal: bool | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, source: Literal["volume"] | Omit = omit, @@ -1023,7 +1057,7 @@ async def create_from_volume_and_poll( volume_id: str, architecture: Literal["aarch64", "x86_64"] | Omit = omit, hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, - hw_machine_type: Optional[Literal["pc", "q35"]] | Omit = omit, + hw_machine_type: Optional[Literal["i440", "q35"]] | Omit = omit, is_baremetal: bool | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, source: Literal["volume"] | Omit = omit, @@ -1094,6 +1128,12 @@ async def get( Retrieve detailed information about a specific image. Args: + project_id: Project ID + + region_id: Region ID + + image_id: Image ID + include_prices: Show price extra_headers: Send extra headers @@ -1137,7 +1177,7 @@ async def upload( architecture: Literal["aarch64", "x86_64"] | Omit = omit, cow_format: bool | Omit = omit, hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, - hw_machine_type: Optional[Literal["pc", "q35"]] | Omit = omit, + hw_machine_type: Optional[Literal["i440", "q35"]] | Omit = omit, is_baremetal: bool | Omit = omit, os_distro: Optional[str] | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, @@ -1157,6 +1197,10 @@ async def upload( like OS type, architecture, and tags. Args: + project_id: Project ID + + region_id: Region ID + name: Image name url: URL @@ -1236,7 +1280,7 @@ async def upload_and_poll( architecture: Literal["aarch64", "x86_64"] | Omit = omit, cow_format: bool | Omit = omit, hw_firmware_type: Optional[Literal["bios", "uefi"]] | Omit = omit, - hw_machine_type: Optional[Literal["pc", "q35"]] | Omit = omit, + hw_machine_type: Optional[Literal["i440", "q35"]] | Omit = omit, is_baremetal: bool | Omit = omit, os_distro: Optional[str] | Omit = omit, os_type: Literal["linux", "windows"] | Omit = omit, diff --git a/src/gcore/resources/cloud/load_balancers/pools/health_monitors.py b/src/gcore/resources/cloud/load_balancers/pools/health_monitors.py index 80b211c9f..93dbbec6f 100644 --- a/src/gcore/resources/cloud/load_balancers/pools/health_monitors.py +++ b/src/gcore/resources/cloud/load_balancers/pools/health_monitors.py @@ -111,8 +111,17 @@ def create( max_retries_down: Number of failures before the member is switched to ERROR state. - url_path: URL Path. Defaults to '/'. Can only be used together with `HTTP` or `HTTPS` - health monitor type. + url_path: The HTTP path the health monitor requests on each member. Defaults to `/` if not + set. Can only be used with `HTTP` or `HTTPS` health monitor type. + + Must start with `/` and contain only plain path segments. Query strings (`?`), + fragments (`#`), percent-encoding (`%`), and consecutive slashes (`//`) are not + allowed. + + Examples of valid paths: + + - `/` — check the root (most common, default) + - `/healthz` — a dedicated health endpoint extra_headers: Send extra headers @@ -296,8 +305,17 @@ async def create( max_retries_down: Number of failures before the member is switched to ERROR state. - url_path: URL Path. Defaults to '/'. Can only be used together with `HTTP` or `HTTPS` - health monitor type. + url_path: The HTTP path the health monitor requests on each member. Defaults to `/` if not + set. Can only be used with `HTTP` or `HTTPS` health monitor type. + + Must start with `/` and contain only plain path segments. Query strings (`?`), + fragments (`#`), percent-encoding (`%`), and consecutive slashes (`//`) are not + allowed. + + Examples of valid paths: + + - `/` — check the root (most common, default) + - `/healthz` — a dedicated health endpoint extra_headers: Send extra headers diff --git a/src/gcore/resources/cloud/networks/routers.py b/src/gcore/resources/cloud/networks/routers.py index 8946358b7..66955ac5f 100644 --- a/src/gcore/resources/cloud/networks/routers.py +++ b/src/gcore/resources/cloud/networks/routers.py @@ -2,7 +2,6 @@ from __future__ import annotations -import typing_extensions from typing import Iterable, Optional import httpx @@ -120,29 +119,32 @@ def create( cast_to=TaskIDList, ) - @typing_extensions.deprecated("deprecated") def update( self, router_id: str, *, project_id: int | None = None, region_id: int | None = None, - external_gateway_info: Optional[router_update_params.ExternalGatewayInfo] | Omit = omit, - name: Optional[str] | Omit = omit, - routes: Optional[Iterable[router_update_params.Route]] | Omit = omit, + external_gateway_info: router_update_params.ExternalGatewayInfo | Omit = omit, + name: str | Omit = omit, + routes: Iterable[router_update_params.Route] | 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, - ) -> Router: - """Update the configuration of an existing router. - - **Deprecated**: Use - `PATCH /v2/routers/{project_id}/{region_id}/{router_id}` instead. + ) -> TaskIDList: + """ + Update the configuration of an existing router. Args: + project_id: Project ID + + region_id: Region ID + + router_id: Router ID + external_gateway_info: New external gateway configuration. Only type 'manual' is accepted on update, so you must provide the `network_id` of the external network. Set to null to remove the external gateway. @@ -167,7 +169,7 @@ def update( raise ValueError(f"Expected a non-empty value for `router_id` but received {router_id!r}") return self._patch( path_template( - "/cloud/v1/routers/{project_id}/{region_id}/{router_id}", + "/cloud/v2/routers/{project_id}/{region_id}/{router_id}", project_id=project_id, region_id=region_id, router_id=router_id, @@ -183,7 +185,57 @@ def update( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=Router, + cast_to=TaskIDList, + ) + + def update_and_poll( + self, + router_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + external_gateway_info: router_update_params.ExternalGatewayInfo | Omit = omit, + name: str | Omit = omit, + routes: Iterable[router_update_params.Route] | Omit = omit, + polling_interval_seconds: int | Omit = omit, + polling_timeout_seconds: int | Omit = omit, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + # 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, + ) -> Router: + """ + Update router and poll for the result. Only the first task will be polled. If you need to poll more tasks, use the `tasks.poll` method. + """ + response = self.update( + router_id=router_id, + project_id=project_id, + region_id=region_id, + external_gateway_info=external_gateway_info, + name=name, + routes=routes, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + if response.tasks: + self._client.cloud.tasks.poll( + task_id=response.tasks[0], + extra_headers=extra_headers, + polling_interval_seconds=polling_interval_seconds, + polling_timeout_seconds=polling_timeout_seconds, + ) + return self.get( + router_id=router_id, + project_id=project_id, + region_id=region_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, ) def list( @@ -536,29 +588,32 @@ async def create( cast_to=TaskIDList, ) - @typing_extensions.deprecated("deprecated") async def update( self, router_id: str, *, project_id: int | None = None, region_id: int | None = None, - external_gateway_info: Optional[router_update_params.ExternalGatewayInfo] | Omit = omit, - name: Optional[str] | Omit = omit, - routes: Optional[Iterable[router_update_params.Route]] | Omit = omit, + external_gateway_info: router_update_params.ExternalGatewayInfo | Omit = omit, + name: str | Omit = omit, + routes: Iterable[router_update_params.Route] | 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, - ) -> Router: - """Update the configuration of an existing router. - - **Deprecated**: Use - `PATCH /v2/routers/{project_id}/{region_id}/{router_id}` instead. + ) -> TaskIDList: + """ + Update the configuration of an existing router. Args: + project_id: Project ID + + region_id: Region ID + + router_id: Router ID + external_gateway_info: New external gateway configuration. Only type 'manual' is accepted on update, so you must provide the `network_id` of the external network. Set to null to remove the external gateway. @@ -583,7 +638,7 @@ async def update( raise ValueError(f"Expected a non-empty value for `router_id` but received {router_id!r}") return await self._patch( path_template( - "/cloud/v1/routers/{project_id}/{region_id}/{router_id}", + "/cloud/v2/routers/{project_id}/{region_id}/{router_id}", project_id=project_id, region_id=region_id, router_id=router_id, @@ -599,7 +654,57 @@ async def update( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=Router, + cast_to=TaskIDList, + ) + + async def update_and_poll( + self, + router_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + external_gateway_info: router_update_params.ExternalGatewayInfo | Omit = omit, + name: str | Omit = omit, + routes: Iterable[router_update_params.Route] | Omit = omit, + polling_interval_seconds: int | Omit = omit, + polling_timeout_seconds: int | Omit = omit, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + # 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, + ) -> Router: + """ + Update router and poll for the result. Only the first task will be polled. If you need to poll more tasks, use the `tasks.poll` method. + """ + response = await self.update( + router_id=router_id, + project_id=project_id, + region_id=region_id, + external_gateway_info=external_gateway_info, + name=name, + routes=routes, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + if response.tasks: + await self._client.cloud.tasks.poll( + task_id=response.tasks[0], + extra_headers=extra_headers, + polling_interval_seconds=polling_interval_seconds, + polling_timeout_seconds=polling_timeout_seconds, + ) + return await self.get( + router_id=router_id, + project_id=project_id, + region_id=region_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, ) def list( @@ -873,10 +978,11 @@ def __init__(self, routers: RoutersResource) -> None: self.create = to_raw_response_wrapper( routers.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - routers.update, # pyright: ignore[reportDeprecated], - ) + self.update = to_raw_response_wrapper( + routers.update, + ) + self.update_and_poll = to_raw_response_wrapper( + routers.update_and_poll, ) self.list = to_raw_response_wrapper( routers.list, @@ -902,10 +1008,11 @@ def __init__(self, routers: AsyncRoutersResource) -> None: self.create = async_to_raw_response_wrapper( routers.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - routers.update, # pyright: ignore[reportDeprecated], - ) + self.update = async_to_raw_response_wrapper( + routers.update, + ) + self.update_and_poll = async_to_raw_response_wrapper( + routers.update_and_poll, ) self.list = async_to_raw_response_wrapper( routers.list, @@ -931,10 +1038,11 @@ def __init__(self, routers: RoutersResource) -> None: self.create = to_streamed_response_wrapper( routers.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - routers.update, # pyright: ignore[reportDeprecated], - ) + self.update = to_streamed_response_wrapper( + routers.update, + ) + self.update_and_poll = to_streamed_response_wrapper( + routers.update_and_poll, ) self.list = to_streamed_response_wrapper( routers.list, @@ -960,10 +1068,11 @@ def __init__(self, routers: AsyncRoutersResource) -> None: self.create = async_to_streamed_response_wrapper( routers.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - routers.update, # pyright: ignore[reportDeprecated], - ) + self.update = async_to_streamed_response_wrapper( + routers.update, + ) + self.update_and_poll = async_to_streamed_response_wrapper( + routers.update_and_poll, ) self.list = async_to_streamed_response_wrapper( routers.list, diff --git a/src/gcore/resources/cloud/tasks.py b/src/gcore/resources/cloud/tasks.py index a362eaf05..606a625a1 100644 --- a/src/gcore/resources/cloud/tasks.py +++ b/src/gcore/resources/cloud/tasks.py @@ -175,21 +175,22 @@ def list( 'patch_lbpool', 'put_into_server_group', 'put_l7rule', 'rebuild_bm', 'rebuild_gpu_baremetal_cluster', 'rebuild_gpu_baremetal_node', 'rebuild_gpu_baremetal_server', 'remove_from_server_group', - 'replace_lbmetadata', 'resize_k8s_cluster_v2', 'resize_loadbalancer', - 'resize_vm', 'resume_vm', 'revert_volume', 'soft_reboot_bm', - 'soft_reboot_gpu_baremetal_cluster', 'soft_reboot_gpu_baremetal_server', - 'soft_reboot_gpu_virtual_cluster', 'soft_reboot_gpu_virtual_server', - 'soft_reboot_vm', 'start_bm', 'start_gpu_baremetal_cluster', - 'start_gpu_baremetal_server', 'start_gpu_virtual_cluster', - 'start_gpu_virtual_server', 'start_vm', 'stop_bm', 'stop_gpu_baremetal_cluster', - 'stop_gpu_baremetal_server', 'stop_gpu_virtual_cluster', - 'stop_gpu_virtual_server', 'stop_vm', 'suspend_vm', 'sync_private_flavors', - 'update_ddos_profile', 'update_floating_ip', 'update_inference_application', - 'update_inference_instance', 'update_k8s_cluster_v2', 'update_l7policy', - 'update_lbmetadata', 'update_loadbalancer', 'update_port_allowed_address_pairs', - 'update_router', 'update_security_group', 'update_sfs', - 'update_tags_gpu_virtual_cluster', 'upgrade_k8s_cluster_v2', - 'upscale_ai_cluster_gpu', 'upscale_gpu_virtual_cluster'] + 'replace_gpu_baremetal_server', 'replace_lbmetadata', 'resize_k8s_cluster_v2', + 'resize_loadbalancer', 'resize_vm', 'resume_vm', 'revert_volume', + 'soft_reboot_bm', 'soft_reboot_gpu_baremetal_cluster', + 'soft_reboot_gpu_baremetal_server', 'soft_reboot_gpu_virtual_cluster', + 'soft_reboot_gpu_virtual_server', 'soft_reboot_vm', 'start_bm', + 'start_gpu_baremetal_cluster', 'start_gpu_baremetal_server', + 'start_gpu_virtual_cluster', 'start_gpu_virtual_server', 'start_vm', 'stop_bm', + 'stop_gpu_baremetal_cluster', 'stop_gpu_baremetal_server', + 'stop_gpu_virtual_cluster', 'stop_gpu_virtual_server', 'stop_vm', 'suspend_vm', + 'sync_private_flavors', 'update_ddos_profile', 'update_floating_ip', + 'update_inference_application', 'update_inference_instance', + 'update_k8s_cluster_v2', 'update_l7policy', 'update_lbmetadata', + 'update_loadbalancer', 'update_port_allowed_address_pairs', 'update_router', + 'update_security_group', 'update_sfs', 'update_tags_gpu_virtual_cluster', + 'upgrade_k8s_cluster_v2', 'upscale_ai_cluster_gpu', + 'upscale_gpu_virtual_cluster'] to_timestamp: ISO formatted datetime string. Filter the tasks by creation date less than or equal to `to_timestamp` @@ -496,21 +497,22 @@ def list( 'patch_lbpool', 'put_into_server_group', 'put_l7rule', 'rebuild_bm', 'rebuild_gpu_baremetal_cluster', 'rebuild_gpu_baremetal_node', 'rebuild_gpu_baremetal_server', 'remove_from_server_group', - 'replace_lbmetadata', 'resize_k8s_cluster_v2', 'resize_loadbalancer', - 'resize_vm', 'resume_vm', 'revert_volume', 'soft_reboot_bm', - 'soft_reboot_gpu_baremetal_cluster', 'soft_reboot_gpu_baremetal_server', - 'soft_reboot_gpu_virtual_cluster', 'soft_reboot_gpu_virtual_server', - 'soft_reboot_vm', 'start_bm', 'start_gpu_baremetal_cluster', - 'start_gpu_baremetal_server', 'start_gpu_virtual_cluster', - 'start_gpu_virtual_server', 'start_vm', 'stop_bm', 'stop_gpu_baremetal_cluster', - 'stop_gpu_baremetal_server', 'stop_gpu_virtual_cluster', - 'stop_gpu_virtual_server', 'stop_vm', 'suspend_vm', 'sync_private_flavors', - 'update_ddos_profile', 'update_floating_ip', 'update_inference_application', - 'update_inference_instance', 'update_k8s_cluster_v2', 'update_l7policy', - 'update_lbmetadata', 'update_loadbalancer', 'update_port_allowed_address_pairs', - 'update_router', 'update_security_group', 'update_sfs', - 'update_tags_gpu_virtual_cluster', 'upgrade_k8s_cluster_v2', - 'upscale_ai_cluster_gpu', 'upscale_gpu_virtual_cluster'] + 'replace_gpu_baremetal_server', 'replace_lbmetadata', 'resize_k8s_cluster_v2', + 'resize_loadbalancer', 'resize_vm', 'resume_vm', 'revert_volume', + 'soft_reboot_bm', 'soft_reboot_gpu_baremetal_cluster', + 'soft_reboot_gpu_baremetal_server', 'soft_reboot_gpu_virtual_cluster', + 'soft_reboot_gpu_virtual_server', 'soft_reboot_vm', 'start_bm', + 'start_gpu_baremetal_cluster', 'start_gpu_baremetal_server', + 'start_gpu_virtual_cluster', 'start_gpu_virtual_server', 'start_vm', 'stop_bm', + 'stop_gpu_baremetal_cluster', 'stop_gpu_baremetal_server', + 'stop_gpu_virtual_cluster', 'stop_gpu_virtual_server', 'stop_vm', 'suspend_vm', + 'sync_private_flavors', 'update_ddos_profile', 'update_floating_ip', + 'update_inference_application', 'update_inference_instance', + 'update_k8s_cluster_v2', 'update_l7policy', 'update_lbmetadata', + 'update_loadbalancer', 'update_port_allowed_address_pairs', 'update_router', + 'update_security_group', 'update_sfs', 'update_tags_gpu_virtual_cluster', + 'upgrade_k8s_cluster_v2', 'upscale_ai_cluster_gpu', + 'upscale_gpu_virtual_cluster'] to_timestamp: ISO formatted datetime string. Filter the tasks by creation date less than or equal to `to_timestamp` diff --git a/src/gcore/resources/iam/api.md b/src/gcore/resources/iam/api.md index b0d13f766..cea662fe4 100644 --- a/src/gcore/resources/iam/api.md +++ b/src/gcore/resources/iam/api.md @@ -20,10 +20,10 @@ from gcore.types.iam import APIToken, APITokenClientUser, APITokenCreated, APITo Methods: -- client.iam.api_tokens.create(client_id, \*\*params) -> APITokenCreated -- client.iam.api_tokens.list(client_id, \*\*params) -> APITokenList -- client.iam.api_tokens.delete(token_id, \*, client_id) -> None -- client.iam.api_tokens.get(token_id, \*, client_id) -> APIToken +- client.iam.api_tokens.create(client_id, \*\*params) -> APITokenCreated +- client.iam.api_tokens.list(client_id, \*\*params) -> APITokenList +- client.iam.api_tokens.delete(token_id, \*, client_id) -> None +- client.iam.api_tokens.get(token_id, \*, client_id) -> APIToken ## Users diff --git a/src/gcore/resources/iam/api_tokens.py b/src/gcore/resources/iam/api_tokens.py index 84369eb9f..36af35e9a 100644 --- a/src/gcore/resources/iam/api_tokens.py +++ b/src/gcore/resources/iam/api_tokens.py @@ -2,7 +2,6 @@ from __future__ import annotations -import typing_extensions from typing import Optional import httpx @@ -66,7 +65,6 @@ def with_streaming_response(self) -> APITokensResourceWithStreamingResponse: """ return APITokensResourceWithStreamingResponse(self) - @typing_extensions.deprecated("deprecated") def create( self, client_id: int, @@ -82,12 +80,15 @@ def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> APITokenCreated: - """**Deprecated:** This endpoint will be removed on **2026-07-17**. + """ + Create an API token in the current account. - Use - [`POST /v2/clients/{clientId}/tokens`](#operation/iamCreateApiTokenV2) instead. + This V2 endpoint generates tokens that use `_` (underscore) as the separator + between the token ID and the secret string, instead of the `$` separator used by + the V1 endpoint. For example: `42_a1b2c3d4...` - Create an API token in the current account. + Tokens created via this endpoint are fully compatible with all existing + authentication mechanisms. Args: client_user: API token role. @@ -108,7 +109,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - path_template("/iam/clients/{client_id}/tokens", client_id=client_id), + path_template("/iam/v2/clients/{client_id}/tokens", client_id=client_id), body=maybe_transform( { "client_user": client_user, @@ -124,7 +125,6 @@ def create( cast_to=APITokenCreated, ) - @typing_extensions.deprecated("deprecated") def list( self, client_id: int, @@ -140,14 +140,13 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> APITokenList: - """**Deprecated:** This endpoint will be removed on **2026-07-17**. - - Use - [`GET /v2/clients/{clientId}/tokens`](#operation/iamGetApiTokensV2) instead. + """Get information about your permanent API tokens in the account. - Get information about your permanent API tokens in the account. A user with the + A user with the Administrators role gets information about all API tokens in the account. + This endpoint is identical to the V1 endpoint. + Args: deleted: The state of API tokens included in the response. Two possible values: @@ -179,7 +178,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get( - path_template("/iam/clients/{client_id}/tokens", client_id=client_id), + path_template("/iam/v2/clients/{client_id}/tokens", client_id=client_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -198,7 +197,6 @@ def list( cast_to=APITokenList, ) - @typing_extensions.deprecated("deprecated") def delete( self, token_id: int, @@ -211,17 +209,15 @@ def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: - """**Deprecated:** This endpoint will be removed on **2026-07-17**. - - Use - [`DELETE /v2/clients/{clientId}/tokens/{tokenId}`](#operation/iamDeleteApiTokenV2) - instead. + """Delete API token from current account. - Delete API token from current account. Ensure that the API token is not being + Ensure that the API token is not being used by an active application. After deleting the token, all applications that use this token will not be able to get access to your account via API. The action cannot be reversed. + This endpoint is identical to the V1 endpoint. + Args: extra_headers: Send extra headers @@ -233,14 +229,13 @@ def delete( """ extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - path_template("/iam/clients/{client_id}/tokens/{token_id}", client_id=client_id, token_id=token_id), + path_template("/iam/v2/clients/{client_id}/tokens/{token_id}", client_id=client_id, token_id=token_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=NoneType, ) - @typing_extensions.deprecated("deprecated") def get( self, token_id: int, @@ -253,11 +248,10 @@ def get( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> APIToken: - """**Deprecated:** This endpoint will be removed on **2026-07-17**. + """ + Get information about a specific API token. - Use - [`GET /v2/clients/{clientId}/tokens/{tokenId}`](#operation/iamGetApiTokenV2) - instead. + This endpoint is identical to the V1 endpoint. Args: extra_headers: Send extra headers @@ -269,7 +263,7 @@ def get( timeout: Override the client-level default timeout for this request, in seconds """ return self._get( - path_template("/iam/clients/{client_id}/tokens/{token_id}", client_id=client_id, token_id=token_id), + path_template("/iam/v2/clients/{client_id}/tokens/{token_id}", client_id=client_id, token_id=token_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -317,7 +311,6 @@ def with_streaming_response(self) -> AsyncAPITokensResourceWithStreamingResponse """ return AsyncAPITokensResourceWithStreamingResponse(self) - @typing_extensions.deprecated("deprecated") async def create( self, client_id: int, @@ -333,12 +326,15 @@ async def create( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> APITokenCreated: - """**Deprecated:** This endpoint will be removed on **2026-07-17**. + """ + Create an API token in the current account. - Use - [`POST /v2/clients/{clientId}/tokens`](#operation/iamCreateApiTokenV2) instead. + This V2 endpoint generates tokens that use `_` (underscore) as the separator + between the token ID and the secret string, instead of the `$` separator used by + the V1 endpoint. For example: `42_a1b2c3d4...` - Create an API token in the current account. + Tokens created via this endpoint are fully compatible with all existing + authentication mechanisms. Args: client_user: API token role. @@ -359,7 +355,7 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - path_template("/iam/clients/{client_id}/tokens", client_id=client_id), + path_template("/iam/v2/clients/{client_id}/tokens", client_id=client_id), body=await async_maybe_transform( { "client_user": client_user, @@ -375,7 +371,6 @@ async def create( cast_to=APITokenCreated, ) - @typing_extensions.deprecated("deprecated") async def list( self, client_id: int, @@ -391,14 +386,13 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> APITokenList: - """**Deprecated:** This endpoint will be removed on **2026-07-17**. - - Use - [`GET /v2/clients/{clientId}/tokens`](#operation/iamGetApiTokensV2) instead. + """Get information about your permanent API tokens in the account. - Get information about your permanent API tokens in the account. A user with the + A user with the Administrators role gets information about all API tokens in the account. + This endpoint is identical to the V1 endpoint. + Args: deleted: The state of API tokens included in the response. Two possible values: @@ -430,7 +424,7 @@ async def list( timeout: Override the client-level default timeout for this request, in seconds """ return await self._get( - path_template("/iam/clients/{client_id}/tokens", client_id=client_id), + path_template("/iam/v2/clients/{client_id}/tokens", client_id=client_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -449,7 +443,6 @@ async def list( cast_to=APITokenList, ) - @typing_extensions.deprecated("deprecated") async def delete( self, token_id: int, @@ -462,17 +455,15 @@ async def delete( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: - """**Deprecated:** This endpoint will be removed on **2026-07-17**. - - Use - [`DELETE /v2/clients/{clientId}/tokens/{tokenId}`](#operation/iamDeleteApiTokenV2) - instead. + """Delete API token from current account. - Delete API token from current account. Ensure that the API token is not being + Ensure that the API token is not being used by an active application. After deleting the token, all applications that use this token will not be able to get access to your account via API. The action cannot be reversed. + This endpoint is identical to the V1 endpoint. + Args: extra_headers: Send extra headers @@ -484,14 +475,13 @@ async def delete( """ extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - path_template("/iam/clients/{client_id}/tokens/{token_id}", client_id=client_id, token_id=token_id), + path_template("/iam/v2/clients/{client_id}/tokens/{token_id}", client_id=client_id, token_id=token_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=NoneType, ) - @typing_extensions.deprecated("deprecated") async def get( self, token_id: int, @@ -504,11 +494,10 @@ async def get( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> APIToken: - """**Deprecated:** This endpoint will be removed on **2026-07-17**. + """ + Get information about a specific API token. - Use - [`GET /v2/clients/{clientId}/tokens/{tokenId}`](#operation/iamGetApiTokenV2) - instead. + This endpoint is identical to the V1 endpoint. Args: extra_headers: Send extra headers @@ -520,7 +509,7 @@ async def get( timeout: Override the client-level default timeout for this request, in seconds """ return await self._get( - path_template("/iam/clients/{client_id}/tokens/{token_id}", client_id=client_id, token_id=token_id), + path_template("/iam/v2/clients/{client_id}/tokens/{token_id}", client_id=client_id, token_id=token_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -532,25 +521,17 @@ class APITokensResourceWithRawResponse: def __init__(self, api_tokens: APITokensResource) -> None: self._api_tokens = api_tokens - self.create = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - api_tokens.create, # pyright: ignore[reportDeprecated], - ) + self.create = to_raw_response_wrapper( + api_tokens.create, ) - self.list = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - api_tokens.list, # pyright: ignore[reportDeprecated], - ) + self.list = to_raw_response_wrapper( + api_tokens.list, ) - self.delete = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - api_tokens.delete, # pyright: ignore[reportDeprecated], - ) + self.delete = to_raw_response_wrapper( + api_tokens.delete, ) - self.get = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - api_tokens.get, # pyright: ignore[reportDeprecated], - ) + self.get = to_raw_response_wrapper( + api_tokens.get, ) @@ -558,25 +539,17 @@ class AsyncAPITokensResourceWithRawResponse: def __init__(self, api_tokens: AsyncAPITokensResource) -> None: self._api_tokens = api_tokens - self.create = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - api_tokens.create, # pyright: ignore[reportDeprecated], - ) + self.create = async_to_raw_response_wrapper( + api_tokens.create, ) - self.list = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - api_tokens.list, # pyright: ignore[reportDeprecated], - ) + self.list = async_to_raw_response_wrapper( + api_tokens.list, ) - self.delete = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - api_tokens.delete, # pyright: ignore[reportDeprecated], - ) + self.delete = async_to_raw_response_wrapper( + api_tokens.delete, ) - self.get = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - api_tokens.get, # pyright: ignore[reportDeprecated], - ) + self.get = async_to_raw_response_wrapper( + api_tokens.get, ) @@ -584,25 +557,17 @@ class APITokensResourceWithStreamingResponse: def __init__(self, api_tokens: APITokensResource) -> None: self._api_tokens = api_tokens - self.create = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - api_tokens.create, # pyright: ignore[reportDeprecated], - ) + self.create = to_streamed_response_wrapper( + api_tokens.create, ) - self.list = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - api_tokens.list, # pyright: ignore[reportDeprecated], - ) + self.list = to_streamed_response_wrapper( + api_tokens.list, ) - self.delete = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - api_tokens.delete, # pyright: ignore[reportDeprecated], - ) + self.delete = to_streamed_response_wrapper( + api_tokens.delete, ) - self.get = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - api_tokens.get, # pyright: ignore[reportDeprecated], - ) + self.get = to_streamed_response_wrapper( + api_tokens.get, ) @@ -610,23 +575,15 @@ class AsyncAPITokensResourceWithStreamingResponse: def __init__(self, api_tokens: AsyncAPITokensResource) -> None: self._api_tokens = api_tokens - self.create = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - api_tokens.create, # pyright: ignore[reportDeprecated], - ) + self.create = async_to_streamed_response_wrapper( + api_tokens.create, ) - self.list = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - api_tokens.list, # pyright: ignore[reportDeprecated], - ) + self.list = async_to_streamed_response_wrapper( + api_tokens.list, ) - self.delete = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - api_tokens.delete, # pyright: ignore[reportDeprecated], - ) + self.delete = async_to_streamed_response_wrapper( + api_tokens.delete, ) - self.get = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - api_tokens.get, # pyright: ignore[reportDeprecated], - ) + self.get = async_to_streamed_response_wrapper( + api_tokens.get, ) diff --git a/src/gcore/resources/storage/api.md b/src/gcore/resources/storage/api.md index 7d0df0d85..a3a72e3e9 100644 --- a/src/gcore/resources/storage/api.md +++ b/src/gcore/resources/storage/api.md @@ -41,6 +41,7 @@ Methods: - client.storage.object_storages.access_keys.create(storage_id) -> AccessKeyCreated - client.storage.object_storages.access_keys.list(storage_id, \*\*params) -> SyncOffsetPage[AccessKey] - client.storage.object_storages.access_keys.delete(access_key, \*, storage_id) -> None +- client.storage.object_storages.access_keys.get(access_key, \*, storage_id) -> AccessKey ### Buckets diff --git a/src/gcore/resources/storage/object_storages/access_keys.py b/src/gcore/resources/storage/object_storages/access_keys.py index c30a03e8f..c650bf433 100644 --- a/src/gcore/resources/storage/object_storages/access_keys.py +++ b/src/gcore/resources/storage/object_storages/access_keys.py @@ -165,6 +165,44 @@ def delete( cast_to=NoneType, ) + def get( + self, + access_key: str, + *, + storage_id: int, + # 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, + ) -> AccessKey: + """ + Returns details of a specific access key for an S3-compatible storage. + + 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 + """ + if not access_key: + raise ValueError(f"Expected a non-empty value for `access_key` but received {access_key!r}") + return self._get( + path_template( + "/storage/v4/object_storages/{storage_id}/access_keys/{access_key}", + storage_id=storage_id, + access_key=access_key, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AccessKey, + ) + class AsyncAccessKeysResource(AsyncAPIResource): @cached_property @@ -308,6 +346,44 @@ async def delete( cast_to=NoneType, ) + async def get( + self, + access_key: str, + *, + storage_id: int, + # 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, + ) -> AccessKey: + """ + Returns details of a specific access key for an S3-compatible storage. + + 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 + """ + if not access_key: + raise ValueError(f"Expected a non-empty value for `access_key` but received {access_key!r}") + return await self._get( + path_template( + "/storage/v4/object_storages/{storage_id}/access_keys/{access_key}", + storage_id=storage_id, + access_key=access_key, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AccessKey, + ) + class AccessKeysResourceWithRawResponse: def __init__(self, access_keys: AccessKeysResource) -> None: @@ -322,6 +398,9 @@ def __init__(self, access_keys: AccessKeysResource) -> None: self.delete = to_raw_response_wrapper( access_keys.delete, ) + self.get = to_raw_response_wrapper( + access_keys.get, + ) class AsyncAccessKeysResourceWithRawResponse: @@ -337,6 +416,9 @@ def __init__(self, access_keys: AsyncAccessKeysResource) -> None: self.delete = async_to_raw_response_wrapper( access_keys.delete, ) + self.get = async_to_raw_response_wrapper( + access_keys.get, + ) class AccessKeysResourceWithStreamingResponse: @@ -352,6 +434,9 @@ def __init__(self, access_keys: AccessKeysResource) -> None: self.delete = to_streamed_response_wrapper( access_keys.delete, ) + self.get = to_streamed_response_wrapper( + access_keys.get, + ) class AsyncAccessKeysResourceWithStreamingResponse: @@ -367,3 +452,6 @@ def __init__(self, access_keys: AsyncAccessKeysResource) -> None: self.delete = async_to_streamed_response_wrapper( access_keys.delete, ) + self.get = async_to_streamed_response_wrapper( + access_keys.get, + ) diff --git a/src/gcore/resources/streaming/streams/streams.py b/src/gcore/resources/streaming/streams/streams.py index 3552af505..f46bd0ded 100644 --- a/src/gcore/resources/streaming/streams/streams.py +++ b/src/gcore/resources/streaming/streams/streams.py @@ -463,7 +463,40 @@ def get( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream: """ - Returns stream details + Returns the complete details of a specific live stream, including its current + state, configuration, and URLs for ingestion and playback. + + Current State You can determine the real-time status of your broadcast using + these fields: + + - `active`: Whether the stream is enabled in your account. + - `live`: `true` if the primary ingest point is receiving a signal. + - Backup fields like `backup_live` show if the redundant ingest is active. + + Ingestion URLs The response includes URLs for various ingestion protocols to + start your broadcast: + + - **RTMP/RTMPS**: `push_url` and `backup_push_url`. + - **SRT**: `push_url_srt` and `backup_push_url_srt`. + - **WebRTC (WHIP)**: `push_url_whip`. + + Playback URLs & Low Latency The platform provides several Adaptive Bitrate (ABR) + playback options. Modern delivery utilizes Common Media Application Format + (CMAF) to achieve Low Latency (±4 seconds): + + - **LL-HLS (HLS CMAF)**: `hls_cmaf_url`. Recommended for most modern players to + get aka Low Latency (LL-HLS). + - **LL-DASH (MPEG-DASH CMAF)**: `dash_url`. Optimized for web and Android to get + aka Low Latency (LL-DASH). + - **Legacy HLS (MPEG-TS)**: `hls_mpegts_url`. + + **Choosing the right link:** Low Latency technology (CMAF) provides the best + low-latency experience but requires a stable internet connection. Use the + **MPEG-TS** URL for outdated legacy devices or in cases where the viewer's + internet connection is unstable and they see buffering, as this format is more + resilient. + + ![Live Preview](https://demo-files.gvideo.io/apidocs/demo-live.gif) Args: extra_headers: Send extra headers @@ -510,7 +543,11 @@ def start_recording( "record_type" parameter of the stream. - If you have access to the premium feature of saving the original stream (so not just transcoded renditions), then the link to the original file will be in - the "origin_url" field. Look at the description of the field how to use it. + the "origin_url" field. The original file is stored for up to 7 days and is + then deleted automatically. By default, custom retention for original recorded + files is not available. For enterprise customers, this retention policy can be + adjusted individually upon request. Look at the description of the field how + to use it. Stream must be live for the recording to start, please check fields "live" and/or "backup_live". After the recording starts, field "recording" will switch @@ -1011,7 +1048,40 @@ async def get( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream: """ - Returns stream details + Returns the complete details of a specific live stream, including its current + state, configuration, and URLs for ingestion and playback. + + Current State You can determine the real-time status of your broadcast using + these fields: + + - `active`: Whether the stream is enabled in your account. + - `live`: `true` if the primary ingest point is receiving a signal. + - Backup fields like `backup_live` show if the redundant ingest is active. + + Ingestion URLs The response includes URLs for various ingestion protocols to + start your broadcast: + + - **RTMP/RTMPS**: `push_url` and `backup_push_url`. + - **SRT**: `push_url_srt` and `backup_push_url_srt`. + - **WebRTC (WHIP)**: `push_url_whip`. + + Playback URLs & Low Latency The platform provides several Adaptive Bitrate (ABR) + playback options. Modern delivery utilizes Common Media Application Format + (CMAF) to achieve Low Latency (±4 seconds): + + - **LL-HLS (HLS CMAF)**: `hls_cmaf_url`. Recommended for most modern players to + get aka Low Latency (LL-HLS). + - **LL-DASH (MPEG-DASH CMAF)**: `dash_url`. Optimized for web and Android to get + aka Low Latency (LL-DASH). + - **Legacy HLS (MPEG-TS)**: `hls_mpegts_url`. + + **Choosing the right link:** Low Latency technology (CMAF) provides the best + low-latency experience but requires a stable internet connection. Use the + **MPEG-TS** URL for outdated legacy devices or in cases where the viewer's + internet connection is unstable and they see buffering, as this format is more + resilient. + + ![Live Preview](https://demo-files.gvideo.io/apidocs/demo-live.gif) Args: extra_headers: Send extra headers @@ -1058,7 +1128,11 @@ async def start_recording( "record_type" parameter of the stream. - If you have access to the premium feature of saving the original stream (so not just transcoded renditions), then the link to the original file will be in - the "origin_url" field. Look at the description of the field how to use it. + the "origin_url" field. The original file is stored for up to 7 days and is + then deleted automatically. By default, custom retention for original recorded + files is not available. For enterprise customers, this retention policy can be + adjusted individually upon request. Look at the description of the field how + to use it. Stream must be live for the recording to start, please check fields "live" and/or "backup_live". After the recording starts, field "recording" will switch diff --git a/src/gcore/resources/waap/analytics.py b/src/gcore/resources/waap/analytics.py index ad390079b..a0a8389f0 100644 --- a/src/gcore/resources/waap/analytics.py +++ b/src/gcore/resources/waap/analytics.py @@ -130,6 +130,12 @@ def get_requests( decision: List[Literal["blocked", "monitored", "allowed", "passed"]] | Omit = omit, domains: Iterable[int] | Omit = omit, end: Optional[str] | Omit = omit, + exclude_countries: SequenceNotStr[str] | Omit = omit, + exclude_domains: Iterable[int] | Omit = omit, + exclude_ips: SequenceNotStr[str] | Omit = omit, + exclude_reference_ids: SequenceNotStr[str] | Omit = omit, + exclude_security_rule_names: SequenceNotStr[str] | Omit = omit, + exclude_session_ids: SequenceNotStr[str] | Omit = omit, http_methods: List[Literal["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"]] | Omit = omit, ips: SequenceNotStr[str] | Omit = omit, limit: int | Omit = omit, @@ -168,6 +174,19 @@ def get_requests( end: Filter data items up to a specified end date in ISO 8601 format. If not provided, defaults to the current date and time. + exclude_countries: Exclude data by a country code of the originating IP address in ISO 3166-1 + alpha-2 format. + + exclude_domains: Exclude data by domain ID. + + exclude_ips: Exclude traffic data by client IP. + + exclude_reference_ids: Exclude data by reference IDs. + + exclude_security_rule_names: Exclude data by name of a security rule matched the request. + + exclude_session_ids: Exclude data by session IDs. + http_methods: Filter by HTTP methods ips: Filter traffic data by client IP. @@ -215,6 +234,12 @@ def get_requests( "decision": decision, "domains": domains, "end": end, + "exclude_countries": exclude_countries, + "exclude_domains": exclude_domains, + "exclude_ips": exclude_ips, + "exclude_reference_ids": exclude_reference_ids, + "exclude_security_rule_names": exclude_security_rule_names, + "exclude_session_ids": exclude_session_ids, "http_methods": http_methods, "ips": ips, "limit": limit, @@ -239,6 +264,7 @@ def get_traffic( *, resolution: Literal["daily", "hourly", "minutely"], start: str, + bucket_size: Optional[Literal[60, 300, 600, 900, 1800, 3600, 7200, 10800, 21600, 43200, 86400]] | Omit = omit, domains: Iterable[int] | Omit = omit, end: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -259,6 +285,9 @@ def get_traffic( start: Filter data items starting from a specified date in ISO 8601 format + bucket_size: Optional explicit aggregation bucket width in seconds. When supplied, + `bucket_size` supersedes `resolution` for aggregation granularity. + domains: List of domain IDs. Empty list means all domains belonging to the current account. @@ -284,6 +313,7 @@ def get_traffic( { "resolution": resolution, "start": start, + "bucket_size": bucket_size, "domains": domains, "end": end, }, @@ -298,10 +328,17 @@ def get_traffic_filtered( *, resolution: Literal["daily", "hourly", "minutely"], start: str, + bucket_size: Optional[Literal[60, 300, 600, 900, 1800, 3600, 7200, 10800, 21600, 43200, 86400]] | Omit = omit, countries: SequenceNotStr[str] | Omit = omit, decision: List[Literal["blocked", "monitored", "allowed", "passed"]] | Omit = omit, domains: Iterable[int] | Omit = omit, end: Optional[str] | Omit = omit, + exclude_countries: SequenceNotStr[str] | Omit = omit, + exclude_domains: Iterable[int] | Omit = omit, + exclude_ips: SequenceNotStr[str] | Omit = omit, + exclude_reference_ids: SequenceNotStr[str] | Omit = omit, + exclude_security_rule_names: SequenceNotStr[str] | Omit = omit, + exclude_session_ids: SequenceNotStr[str] | Omit = omit, http_methods: List[Literal["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"]] | Omit = omit, ips: SequenceNotStr[str] | Omit = omit, optional_action: List[Literal["captcha", "challenge"]] | Omit = omit, @@ -329,6 +366,9 @@ def get_traffic_filtered( start: Filter data items starting from a specified date in ISO 8601 format + bucket_size: Optional explicit aggregation bucket width in seconds. When supplied, + `bucket_size` supersedes `resolution` for aggregation granularity. + countries: Filter data by a country code of the originating IP address in ISO 3166-1 alpha-2 format. @@ -340,6 +380,19 @@ def get_traffic_filtered( end: Filter data items up to a specified end date in ISO 8601 format. If not provided, defaults to the current date and time. + exclude_countries: Exclude data by a country code of the originating IP address in ISO 3166-1 + alpha-2 format. + + exclude_domains: Exclude data by domain ID. + + exclude_ips: Exclude traffic data by client IP. + + exclude_reference_ids: Exclude data by reference IDs. + + exclude_security_rule_names: Exclude data by name of a security rule matched the request. + + exclude_session_ids: Exclude data by session IDs. + http_methods: Filter by HTTP methods ips: Filter traffic data by client IP. @@ -377,10 +430,17 @@ def get_traffic_filtered( { "resolution": resolution, "start": start, + "bucket_size": bucket_size, "countries": countries, "decision": decision, "domains": domains, "end": end, + "exclude_countries": exclude_countries, + "exclude_domains": exclude_domains, + "exclude_ips": exclude_ips, + "exclude_reference_ids": exclude_reference_ids, + "exclude_security_rule_names": exclude_security_rule_names, + "exclude_session_ids": exclude_session_ids, "http_methods": http_methods, "ips": ips, "optional_action": optional_action, @@ -495,6 +555,12 @@ def get_requests( decision: List[Literal["blocked", "monitored", "allowed", "passed"]] | Omit = omit, domains: Iterable[int] | Omit = omit, end: Optional[str] | Omit = omit, + exclude_countries: SequenceNotStr[str] | Omit = omit, + exclude_domains: Iterable[int] | Omit = omit, + exclude_ips: SequenceNotStr[str] | Omit = omit, + exclude_reference_ids: SequenceNotStr[str] | Omit = omit, + exclude_security_rule_names: SequenceNotStr[str] | Omit = omit, + exclude_session_ids: SequenceNotStr[str] | Omit = omit, http_methods: List[Literal["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"]] | Omit = omit, ips: SequenceNotStr[str] | Omit = omit, limit: int | Omit = omit, @@ -533,6 +599,19 @@ def get_requests( end: Filter data items up to a specified end date in ISO 8601 format. If not provided, defaults to the current date and time. + exclude_countries: Exclude data by a country code of the originating IP address in ISO 3166-1 + alpha-2 format. + + exclude_domains: Exclude data by domain ID. + + exclude_ips: Exclude traffic data by client IP. + + exclude_reference_ids: Exclude data by reference IDs. + + exclude_security_rule_names: Exclude data by name of a security rule matched the request. + + exclude_session_ids: Exclude data by session IDs. + http_methods: Filter by HTTP methods ips: Filter traffic data by client IP. @@ -580,6 +659,12 @@ def get_requests( "decision": decision, "domains": domains, "end": end, + "exclude_countries": exclude_countries, + "exclude_domains": exclude_domains, + "exclude_ips": exclude_ips, + "exclude_reference_ids": exclude_reference_ids, + "exclude_security_rule_names": exclude_security_rule_names, + "exclude_session_ids": exclude_session_ids, "http_methods": http_methods, "ips": ips, "limit": limit, @@ -604,6 +689,7 @@ async def get_traffic( *, resolution: Literal["daily", "hourly", "minutely"], start: str, + bucket_size: Optional[Literal[60, 300, 600, 900, 1800, 3600, 7200, 10800, 21600, 43200, 86400]] | Omit = omit, domains: Iterable[int] | Omit = omit, end: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -624,6 +710,9 @@ async def get_traffic( start: Filter data items starting from a specified date in ISO 8601 format + bucket_size: Optional explicit aggregation bucket width in seconds. When supplied, + `bucket_size` supersedes `resolution` for aggregation granularity. + domains: List of domain IDs. Empty list means all domains belonging to the current account. @@ -649,6 +738,7 @@ async def get_traffic( { "resolution": resolution, "start": start, + "bucket_size": bucket_size, "domains": domains, "end": end, }, @@ -663,10 +753,17 @@ async def get_traffic_filtered( *, resolution: Literal["daily", "hourly", "minutely"], start: str, + bucket_size: Optional[Literal[60, 300, 600, 900, 1800, 3600, 7200, 10800, 21600, 43200, 86400]] | Omit = omit, countries: SequenceNotStr[str] | Omit = omit, decision: List[Literal["blocked", "monitored", "allowed", "passed"]] | Omit = omit, domains: Iterable[int] | Omit = omit, end: Optional[str] | Omit = omit, + exclude_countries: SequenceNotStr[str] | Omit = omit, + exclude_domains: Iterable[int] | Omit = omit, + exclude_ips: SequenceNotStr[str] | Omit = omit, + exclude_reference_ids: SequenceNotStr[str] | Omit = omit, + exclude_security_rule_names: SequenceNotStr[str] | Omit = omit, + exclude_session_ids: SequenceNotStr[str] | Omit = omit, http_methods: List[Literal["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"]] | Omit = omit, ips: SequenceNotStr[str] | Omit = omit, optional_action: List[Literal["captcha", "challenge"]] | Omit = omit, @@ -694,6 +791,9 @@ async def get_traffic_filtered( start: Filter data items starting from a specified date in ISO 8601 format + bucket_size: Optional explicit aggregation bucket width in seconds. When supplied, + `bucket_size` supersedes `resolution` for aggregation granularity. + countries: Filter data by a country code of the originating IP address in ISO 3166-1 alpha-2 format. @@ -705,6 +805,19 @@ async def get_traffic_filtered( end: Filter data items up to a specified end date in ISO 8601 format. If not provided, defaults to the current date and time. + exclude_countries: Exclude data by a country code of the originating IP address in ISO 3166-1 + alpha-2 format. + + exclude_domains: Exclude data by domain ID. + + exclude_ips: Exclude traffic data by client IP. + + exclude_reference_ids: Exclude data by reference IDs. + + exclude_security_rule_names: Exclude data by name of a security rule matched the request. + + exclude_session_ids: Exclude data by session IDs. + http_methods: Filter by HTTP methods ips: Filter traffic data by client IP. @@ -742,10 +855,17 @@ async def get_traffic_filtered( { "resolution": resolution, "start": start, + "bucket_size": bucket_size, "countries": countries, "decision": decision, "domains": domains, "end": end, + "exclude_countries": exclude_countries, + "exclude_domains": exclude_domains, + "exclude_ips": exclude_ips, + "exclude_reference_ids": exclude_reference_ids, + "exclude_security_rule_names": exclude_security_rule_names, + "exclude_session_ids": exclude_session_ids, "http_methods": http_methods, "ips": ips, "optional_action": optional_action, diff --git a/src/gcore/types/cloud/audit_log_entry.py b/src/gcore/types/cloud/audit_log_entry.py index 80a077ae7..864b1ae39 100644 --- a/src/gcore/types/cloud/audit_log_entry.py +++ b/src/gcore/types/cloud/audit_log_entry.py @@ -143,6 +143,7 @@ class AuditLogEntry(BaseModel): "rebuild", "regenerate_credentials", "remove_from_servergroup", + "replace", "replace_metadata", "resize", "resume", diff --git a/src/gcore/types/cloud/audit_log_list_params.py b/src/gcore/types/cloud/audit_log_list_params.py index 337403f6f..95b6728ea 100644 --- a/src/gcore/types/cloud/audit_log_list_params.py +++ b/src/gcore/types/cloud/audit_log_list_params.py @@ -37,6 +37,7 @@ class AuditLogListParams(TypedDict, total=False): "rebuild", "regenerate_credentials", "remove_from_servergroup", + "replace", "replace_metadata", "resize", "resume", diff --git a/src/gcore/types/cloud/baremetal/baremetal_image.py b/src/gcore/types/cloud/baremetal/baremetal_image.py index 91a534d1f..aa64c662d 100644 --- a/src/gcore/types/cloud/baremetal/baremetal_image.py +++ b/src/gcore/types/cloud/baremetal/baremetal_image.py @@ -46,7 +46,7 @@ class BaremetalImage(BaseModel): hw_firmware_type: Optional[Literal["bios", "uefi"]] = None """Specifies the type of firmware with which to boot the guest.""" - hw_machine_type: Optional[Literal["pc", "q35"]] = None + hw_machine_type: Optional[Literal["i440", "q35"]] = None """A virtual chipset type.""" is_baremetal: Optional[bool] = None diff --git a/src/gcore/types/cloud/databases/postgres/__init__.py b/src/gcore/types/cloud/databases/postgres/__init__.py index d33446a05..e4449e2be 100644 --- a/src/gcore/types/cloud/databases/postgres/__init__.py +++ b/src/gcore/types/cloud/databases/postgres/__init__.py @@ -3,12 +3,10 @@ from __future__ import annotations from .postgres_cluster import PostgresCluster as PostgresCluster +from .pg_conf_validation import PgConfValidation as PgConfValidation from .cluster_list_params import ClusterListParams as ClusterListParams from .cluster_create_params import ClusterCreateParams as ClusterCreateParams from .cluster_update_params import ClusterUpdateParams as ClusterUpdateParams from .postgres_cluster_short import PostgresClusterShort as PostgresClusterShort from .postgres_configuration import PostgresConfiguration as PostgresConfiguration from .custom_configuration_validate_params import CustomConfigurationValidateParams as CustomConfigurationValidateParams -from .custom_configuration_validate_response import ( - CustomConfigurationValidateResponse as CustomConfigurationValidateResponse, -) diff --git a/src/gcore/types/cloud/databases/postgres/custom_configuration_validate_response.py b/src/gcore/types/cloud/databases/postgres/pg_conf_validation.py similarity index 70% rename from src/gcore/types/cloud/databases/postgres/custom_configuration_validate_response.py rename to src/gcore/types/cloud/databases/postgres/pg_conf_validation.py index 995a4a141..8e4ae2322 100644 --- a/src/gcore/types/cloud/databases/postgres/custom_configuration_validate_response.py +++ b/src/gcore/types/cloud/databases/postgres/pg_conf_validation.py @@ -4,10 +4,10 @@ from ....._models import BaseModel -__all__ = ["CustomConfigurationValidateResponse"] +__all__ = ["PgConfValidation"] -class CustomConfigurationValidateResponse(BaseModel): +class PgConfValidation(BaseModel): errors: List[str] """Errors list""" diff --git a/src/gcore/types/cloud/gpu_baremetal/cluster_create_params.py b/src/gcore/types/cloud/gpu_baremetal/cluster_create_params.py index ed821b5d4..33b55fbe4 100644 --- a/src/gcore/types/cloud/gpu_baremetal/cluster_create_params.py +++ b/src/gcore/types/cloud/gpu_baremetal/cluster_create_params.py @@ -160,7 +160,10 @@ class ServersSettings(TypedDict, total=False): """List of file shares to be mounted across the cluster.""" security_groups: Iterable[ServersSettingsSecurityGroup] - """List of security groups UUIDs""" + """List of security group UUIDs. + + If omitted or an empty list, the default security group will be used. + """ user_data: str """Optional custom user data (Base64-encoded)""" diff --git a/src/gcore/types/cloud/gpu_virtual/cluster_create_params.py b/src/gcore/types/cloud/gpu_virtual/cluster_create_params.py index 762e48792..9cdd74f4e 100644 --- a/src/gcore/types/cloud/gpu_virtual/cluster_create_params.py +++ b/src/gcore/types/cloud/gpu_virtual/cluster_create_params.py @@ -215,7 +215,10 @@ class ServersSettings(TypedDict, total=False): """List of file shares to be mounted across the cluster.""" security_groups: Iterable[ServersSettingsSecurityGroup] - """List of security groups UUIDs""" + """List of security group UUIDs. + + If omitted or an empty list, the default security group will be used. + """ user_data: str """Optional custom user data (Base64-encoded)""" diff --git a/src/gcore/types/cloud/instances/image.py b/src/gcore/types/cloud/instances/image.py index 823b6ce50..8c39009c0 100644 --- a/src/gcore/types/cloud/instances/image.py +++ b/src/gcore/types/cloud/instances/image.py @@ -46,7 +46,7 @@ class Image(BaseModel): hw_firmware_type: Optional[Literal["bios", "uefi"]] = None """Specifies the type of firmware with which to boot the guest.""" - hw_machine_type: Optional[Literal["pc", "q35"]] = None + hw_machine_type: Optional[Literal["i440", "q35"]] = None """A virtual chipset type.""" is_baremetal: bool diff --git a/src/gcore/types/cloud/instances/image_create_from_volume_params.py b/src/gcore/types/cloud/instances/image_create_from_volume_params.py index beb3f64f4..326165e0a 100644 --- a/src/gcore/types/cloud/instances/image_create_from_volume_params.py +++ b/src/gcore/types/cloud/instances/image_create_from_volume_params.py @@ -27,7 +27,7 @@ class ImageCreateFromVolumeParams(TypedDict, total=False): hw_firmware_type: Optional[Literal["bios", "uefi"]] """Specifies the type of firmware with which to boot the guest.""" - hw_machine_type: Optional[Literal["pc", "q35"]] + hw_machine_type: Optional[Literal["i440", "q35"]] """A virtual chipset type.""" is_baremetal: bool diff --git a/src/gcore/types/cloud/instances/image_get_params.py b/src/gcore/types/cloud/instances/image_get_params.py index f9d96463a..6cbde65be 100644 --- a/src/gcore/types/cloud/instances/image_get_params.py +++ b/src/gcore/types/cloud/instances/image_get_params.py @@ -9,8 +9,10 @@ class ImageGetParams(TypedDict, total=False): project_id: int + """Project ID""" region_id: int + """Region ID""" include_prices: bool """Show price""" diff --git a/src/gcore/types/cloud/instances/image_update_params.py b/src/gcore/types/cloud/instances/image_update_params.py index d51c715f3..452128c98 100644 --- a/src/gcore/types/cloud/instances/image_update_params.py +++ b/src/gcore/types/cloud/instances/image_update_params.py @@ -2,6 +2,7 @@ from __future__ import annotations +from typing import Optional from typing_extensions import Literal, TypedDict from ..tag_update_map_param import TagUpdateMapParam @@ -11,16 +12,18 @@ class ImageUpdateParams(TypedDict, total=False): project_id: int + """Project ID""" region_id: int + """Region ID""" hw_firmware_type: Literal["bios", "uefi"] """Specifies the type of firmware with which to boot the guest.""" - hw_machine_type: Literal["pc", "q35"] + hw_machine_type: Literal["i440", "q35"] """A virtual chipset type.""" - is_baremetal: bool + is_baremetal: Optional[bool] """Set to true if the image will be used by bare metal servers.""" name: str diff --git a/src/gcore/types/cloud/instances/image_upload_params.py b/src/gcore/types/cloud/instances/image_upload_params.py index 4085ea544..68c0f4136 100644 --- a/src/gcore/types/cloud/instances/image_upload_params.py +++ b/src/gcore/types/cloud/instances/image_upload_params.py @@ -10,8 +10,10 @@ class ImageUploadParams(TypedDict, total=False): project_id: int + """Project ID""" region_id: int + """Region ID""" name: Required[str] """Image name""" @@ -31,7 +33,7 @@ class ImageUploadParams(TypedDict, total=False): hw_firmware_type: Optional[Literal["bios", "uefi"]] """Specifies the type of firmware with which to boot the guest.""" - hw_machine_type: Optional[Literal["pc", "q35"]] + hw_machine_type: Optional[Literal["i440", "q35"]] """A virtual chipset type.""" is_baremetal: bool diff --git a/src/gcore/types/cloud/load_balancer_create_params.py b/src/gcore/types/cloud/load_balancer_create_params.py index 24e0724bc..8ac23cd10 100644 --- a/src/gcore/types/cloud/load_balancer_create_params.py +++ b/src/gcore/types/cloud/load_balancer_create_params.py @@ -186,10 +186,19 @@ class ListenerPoolHealthmonitor(TypedDict, total=False): """Number of failures before the member is switched to ERROR state.""" url_path: Optional[str] - """URL Path. + """The HTTP path the health monitor requests on each member. - Defaults to '/'. Can only be used together with `HTTP` or `HTTPS` health monitor - type. + Defaults to `/` if not set. Can only be used with `HTTP` or `HTTPS` health + monitor type. + + Must start with `/` and contain only plain path segments. Query strings (`?`), + fragments (`#`), percent-encoding (`%`), and consecutive slashes (`//`) are not + allowed. + + Examples of valid paths: + + - `/` — check the root (most common, default) + - `/healthz` — a dedicated health endpoint """ diff --git a/src/gcore/types/cloud/load_balancers/pool_create_params.py b/src/gcore/types/cloud/load_balancers/pool_create_params.py index 9a6944f73..4e1c329e9 100644 --- a/src/gcore/types/cloud/load_balancers/pool_create_params.py +++ b/src/gcore/types/cloud/load_balancers/pool_create_params.py @@ -120,10 +120,19 @@ class Healthmonitor(TypedDict, total=False): """Number of failures before the member is switched to ERROR state.""" url_path: Optional[str] - """URL Path. + """The HTTP path the health monitor requests on each member. - Defaults to '/'. Can only be used together with `HTTP` or `HTTPS` health monitor - type. + Defaults to `/` if not set. Can only be used with `HTTP` or `HTTPS` health + monitor type. + + Must start with `/` and contain only plain path segments. Query strings (`?`), + fragments (`#`), percent-encoding (`%`), and consecutive slashes (`//`) are not + allowed. + + Examples of valid paths: + + - `/` — check the root (most common, default) + - `/healthz` — a dedicated health endpoint """ diff --git a/src/gcore/types/cloud/load_balancers/pool_update_params.py b/src/gcore/types/cloud/load_balancers/pool_update_params.py index a1ac72ebc..df24384a9 100644 --- a/src/gcore/types/cloud/load_balancers/pool_update_params.py +++ b/src/gcore/types/cloud/load_balancers/pool_update_params.py @@ -125,10 +125,19 @@ class Healthmonitor(TypedDict, total=False): """Health monitor type. Once health monitor is created, cannot be changed.""" url_path: Optional[str] - """URL Path. + """The HTTP path the health monitor requests on each member. - Defaults to '/'. Can only be used together with `HTTP` or `HTTPS` health monitor - type. + Defaults to `/` if not set. Can only be used with `HTTP` or `HTTPS` health + monitor type. + + Must start with `/` and contain only plain path segments. Query strings (`?`), + fragments (`#`), percent-encoding (`%`), and consecutive slashes (`//`) are not + allowed. + + Examples of valid paths: + + - `/` — check the root (most common, default) + - `/healthz` — a dedicated health endpoint """ diff --git a/src/gcore/types/cloud/load_balancers/pools/health_monitor_create_params.py b/src/gcore/types/cloud/load_balancers/pools/health_monitor_create_params.py index 525f48e75..74397fce2 100644 --- a/src/gcore/types/cloud/load_balancers/pools/health_monitor_create_params.py +++ b/src/gcore/types/cloud/load_balancers/pools/health_monitor_create_params.py @@ -69,8 +69,17 @@ class HealthMonitorCreateParams(TypedDict, total=False): """Number of failures before the member is switched to ERROR state.""" url_path: Optional[str] - """URL Path. + """The HTTP path the health monitor requests on each member. - Defaults to '/'. Can only be used together with `HTTP` or `HTTPS` health monitor - type. + Defaults to `/` if not set. Can only be used with `HTTP` or `HTTPS` health + monitor type. + + Must start with `/` and contain only plain path segments. Query strings (`?`), + fragments (`#`), percent-encoding (`%`), and consecutive slashes (`//`) are not + allowed. + + Examples of valid paths: + + - `/` — check the root (most common, default) + - `/healthz` — a dedicated health endpoint """ diff --git a/src/gcore/types/cloud/networks/router_update_params.py b/src/gcore/types/cloud/networks/router_update_params.py index 6be4127d5..5d7660969 100644 --- a/src/gcore/types/cloud/networks/router_update_params.py +++ b/src/gcore/types/cloud/networks/router_update_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Iterable, Optional +from typing import Iterable from typing_extensions import Literal, Required, TypedDict __all__ = ["RouterUpdateParams", "ExternalGatewayInfo", "Route"] @@ -10,20 +10,22 @@ class RouterUpdateParams(TypedDict, total=False): project_id: int + """Project ID""" region_id: int + """Region ID""" - external_gateway_info: Optional[ExternalGatewayInfo] + external_gateway_info: ExternalGatewayInfo """New external gateway configuration. Only type 'manual' is accepted on update, so you must provide the `network_id` of the external network. Set to null to remove the external gateway. """ - name: Optional[str] + name: str """New name of router""" - routes: Optional[Iterable[Route]] + routes: Iterable[Route] """List of custom routes.""" diff --git a/src/gcore/types/cloud/task_list_params.py b/src/gcore/types/cloud/task_list_params.py index 9d63651c3..4d77f0b22 100644 --- a/src/gcore/types/cloud/task_list_params.py +++ b/src/gcore/types/cloud/task_list_params.py @@ -94,21 +94,22 @@ class TaskListParams(TypedDict, total=False): 'patch_lbpool', 'put_into_server_group', 'put_l7rule', 'rebuild_bm', 'rebuild_gpu_baremetal_cluster', 'rebuild_gpu_baremetal_node', 'rebuild_gpu_baremetal_server', 'remove_from_server_group', - 'replace_lbmetadata', 'resize_k8s_cluster_v2', 'resize_loadbalancer', - 'resize_vm', 'resume_vm', 'revert_volume', 'soft_reboot_bm', - 'soft_reboot_gpu_baremetal_cluster', 'soft_reboot_gpu_baremetal_server', - 'soft_reboot_gpu_virtual_cluster', 'soft_reboot_gpu_virtual_server', - 'soft_reboot_vm', 'start_bm', 'start_gpu_baremetal_cluster', - 'start_gpu_baremetal_server', 'start_gpu_virtual_cluster', - 'start_gpu_virtual_server', 'start_vm', 'stop_bm', 'stop_gpu_baremetal_cluster', - 'stop_gpu_baremetal_server', 'stop_gpu_virtual_cluster', - 'stop_gpu_virtual_server', 'stop_vm', 'suspend_vm', 'sync_private_flavors', - 'update_ddos_profile', 'update_floating_ip', 'update_inference_application', - 'update_inference_instance', 'update_k8s_cluster_v2', 'update_l7policy', - 'update_lbmetadata', 'update_loadbalancer', 'update_port_allowed_address_pairs', - 'update_router', 'update_security_group', 'update_sfs', - 'update_tags_gpu_virtual_cluster', 'upgrade_k8s_cluster_v2', - 'upscale_ai_cluster_gpu', 'upscale_gpu_virtual_cluster'] + 'replace_gpu_baremetal_server', 'replace_lbmetadata', 'resize_k8s_cluster_v2', + 'resize_loadbalancer', 'resize_vm', 'resume_vm', 'revert_volume', + 'soft_reboot_bm', 'soft_reboot_gpu_baremetal_cluster', + 'soft_reboot_gpu_baremetal_server', 'soft_reboot_gpu_virtual_cluster', + 'soft_reboot_gpu_virtual_server', 'soft_reboot_vm', 'start_bm', + 'start_gpu_baremetal_cluster', 'start_gpu_baremetal_server', + 'start_gpu_virtual_cluster', 'start_gpu_virtual_server', 'start_vm', 'stop_bm', + 'stop_gpu_baremetal_cluster', 'stop_gpu_baremetal_server', + 'stop_gpu_virtual_cluster', 'stop_gpu_virtual_server', 'stop_vm', 'suspend_vm', + 'sync_private_flavors', 'update_ddos_profile', 'update_floating_ip', + 'update_inference_application', 'update_inference_instance', + 'update_k8s_cluster_v2', 'update_l7policy', 'update_lbmetadata', + 'update_loadbalancer', 'update_port_allowed_address_pairs', 'update_router', + 'update_security_group', 'update_sfs', 'update_tags_gpu_virtual_cluster', + 'upgrade_k8s_cluster_v2', 'upscale_ai_cluster_gpu', + 'upscale_gpu_virtual_cluster'] """ to_timestamp: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] diff --git a/src/gcore/types/iam/api_token_created.py b/src/gcore/types/iam/api_token_created.py index c227c690d..6aee8dcf5 100644 --- a/src/gcore/types/iam/api_token_created.py +++ b/src/gcore/types/iam/api_token_created.py @@ -10,6 +10,7 @@ class APITokenCreated(APIToken): token: Optional[str] = None """ - API token. Copy it, because you will not be able to get it again. We do not - store tokens. All responsibility for token storage and usage is on the issuer. + API token with `_` separator. Copy it, because you will not be able to get it + again. We do not store tokens. All responsibility for token storage and usage is + on the issuer. """ diff --git a/src/gcore/types/streaming/stream.py b/src/gcore/types/streaming/stream.py index e096496b2..9f44f4156 100644 --- a/src/gcore/types/streaming/stream.py +++ b/src/gcore/types/streaming/stream.py @@ -27,6 +27,14 @@ class Stream(BaseModel): id: Optional[int] = None """Stream ID""" + token: Optional[str] = None + """Push ingest token for this stream. + + Used in RTMP/RTMPS, SRT, and WebRTC WHIP ingest URLs to authenticate the stream + source, example: + `rtmps://stream.domain.com:443/in/1234567?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`. + """ + active: Optional[bool] = None """Stream switch between on and off. diff --git a/src/gcore/types/streaming/video.py b/src/gcore/types/streaming/video.py index f6a405ee9..adce61ea4 100644 --- a/src/gcore/types/streaming/video.py +++ b/src/gcore/types/streaming/video.py @@ -375,14 +375,18 @@ class Video(BaseModel): MP4 rendition. The MP4 file becomes available for downloading when the video entity "status" - changes from "new" to "pending". The file is stored for 7 days, after which it - will be automatically deleted. + changes from "new" to "pending". Format of URL is `/videos/_/origin__.mp4` Where: - `` – Encoding bitrate in Kbps. - `` – Video height. + The original file is stored for up to 7 days, after which it is deleted + automatically. By default, the retention policy for original recorded files + cannot be changed. For enterprise customers, it can be adjusted individually + upon request. + This is a premium feature, available only upon request through your manager or support team. """ diff --git a/src/gcore/types/waap/analytics_get_requests_params.py b/src/gcore/types/waap/analytics_get_requests_params.py index 6dbb7d921..361e70ce4 100644 --- a/src/gcore/types/waap/analytics_get_requests_params.py +++ b/src/gcore/types/waap/analytics_get_requests_params.py @@ -35,6 +35,27 @@ class AnalyticsGetRequestsParams(TypedDict, total=False): If not provided, defaults to the current date and time. """ + exclude_countries: SequenceNotStr[str] + """ + Exclude data by a country code of the originating IP address in ISO 3166-1 + alpha-2 format. + """ + + exclude_domains: Iterable[int] + """Exclude data by domain ID.""" + + exclude_ips: SequenceNotStr[str] + """Exclude traffic data by client IP.""" + + exclude_reference_ids: SequenceNotStr[str] + """Exclude data by reference IDs.""" + + exclude_security_rule_names: SequenceNotStr[str] + """Exclude data by name of a security rule matched the request.""" + + exclude_session_ids: SequenceNotStr[str] + """Exclude data by session IDs.""" + http_methods: List[Literal["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"]] """Filter by HTTP methods""" diff --git a/src/gcore/types/waap/analytics_get_traffic_filtered_params.py b/src/gcore/types/waap/analytics_get_traffic_filtered_params.py index 03295f075..211e14465 100644 --- a/src/gcore/types/waap/analytics_get_traffic_filtered_params.py +++ b/src/gcore/types/waap/analytics_get_traffic_filtered_params.py @@ -17,6 +17,13 @@ class AnalyticsGetTrafficFilteredParams(TypedDict, total=False): start: Required[str] """Filter data items starting from a specified date in ISO 8601 format""" + bucket_size: Optional[Literal[60, 300, 600, 900, 1800, 3600, 7200, 10800, 21600, 43200, 86400]] + """Optional explicit aggregation bucket width in seconds. + + When supplied, `bucket_size` supersedes `resolution` for aggregation + granularity. + """ + countries: SequenceNotStr[str] """ Filter data by a country code of the originating IP address in ISO 3166-1 @@ -38,6 +45,27 @@ class AnalyticsGetTrafficFilteredParams(TypedDict, total=False): If not provided, defaults to the current date and time. """ + exclude_countries: SequenceNotStr[str] + """ + Exclude data by a country code of the originating IP address in ISO 3166-1 + alpha-2 format. + """ + + exclude_domains: Iterable[int] + """Exclude data by domain ID.""" + + exclude_ips: SequenceNotStr[str] + """Exclude traffic data by client IP.""" + + exclude_reference_ids: SequenceNotStr[str] + """Exclude data by reference IDs.""" + + exclude_security_rule_names: SequenceNotStr[str] + """Exclude data by name of a security rule matched the request.""" + + exclude_session_ids: SequenceNotStr[str] + """Exclude data by session IDs.""" + http_methods: List[Literal["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"]] """Filter by HTTP methods""" diff --git a/src/gcore/types/waap/analytics_get_traffic_params.py b/src/gcore/types/waap/analytics_get_traffic_params.py index 41c6d623a..27cbe033f 100644 --- a/src/gcore/types/waap/analytics_get_traffic_params.py +++ b/src/gcore/types/waap/analytics_get_traffic_params.py @@ -15,6 +15,13 @@ class AnalyticsGetTrafficParams(TypedDict, total=False): start: Required[str] """Filter data items starting from a specified date in ISO 8601 format""" + bucket_size: Optional[Literal[60, 300, 600, 900, 1800, 3600, 7200, 10800, 21600, 43200, 86400]] + """Optional explicit aggregation bucket width in seconds. + + When supplied, `bucket_size` supersedes `resolution` for aggregation + granularity. + """ + domains: Iterable[int] """List of domain IDs. diff --git a/tests/api_resources/cloud/databases/postgres/test_custom_configurations.py b/tests/api_resources/cloud/databases/postgres/test_custom_configurations.py index 129a97816..1f6935d3b 100644 --- a/tests/api_resources/cloud/databases/postgres/test_custom_configurations.py +++ b/tests/api_resources/cloud/databases/postgres/test_custom_configurations.py @@ -9,9 +9,7 @@ from gcore import Gcore, AsyncGcore from tests.utils import assert_matches_type -from gcore.types.cloud.databases.postgres import ( - CustomConfigurationValidateResponse, -) +from gcore.types.cloud.databases.postgres import PgConfValidation base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -27,7 +25,7 @@ def test_method_validate(self, client: Gcore) -> None: pg_conf="\nlisten_addresses = 'localhost'\nport = 5432\nmax_connections = 100\nshared_buffers = 128MB\nlogging_collector = on", version="15", ) - assert_matches_type(CustomConfigurationValidateResponse, custom_configuration, path=["response"]) + assert_matches_type(PgConfValidation, custom_configuration, path=["response"]) @parametrize def test_raw_response_validate(self, client: Gcore) -> None: @@ -41,7 +39,7 @@ def test_raw_response_validate(self, client: Gcore) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" custom_configuration = response.parse() - assert_matches_type(CustomConfigurationValidateResponse, custom_configuration, path=["response"]) + assert_matches_type(PgConfValidation, custom_configuration, path=["response"]) @parametrize def test_streaming_response_validate(self, client: Gcore) -> None: @@ -55,7 +53,7 @@ def test_streaming_response_validate(self, client: Gcore) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" custom_configuration = response.parse() - assert_matches_type(CustomConfigurationValidateResponse, custom_configuration, path=["response"]) + assert_matches_type(PgConfValidation, custom_configuration, path=["response"]) assert cast(Any, response.is_closed) is True @@ -73,7 +71,7 @@ async def test_method_validate(self, async_client: AsyncGcore) -> None: pg_conf="\nlisten_addresses = 'localhost'\nport = 5432\nmax_connections = 100\nshared_buffers = 128MB\nlogging_collector = on", version="15", ) - assert_matches_type(CustomConfigurationValidateResponse, custom_configuration, path=["response"]) + assert_matches_type(PgConfValidation, custom_configuration, path=["response"]) @parametrize async def test_raw_response_validate(self, async_client: AsyncGcore) -> None: @@ -87,7 +85,7 @@ async def test_raw_response_validate(self, async_client: AsyncGcore) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" custom_configuration = await response.parse() - assert_matches_type(CustomConfigurationValidateResponse, custom_configuration, path=["response"]) + assert_matches_type(PgConfValidation, custom_configuration, path=["response"]) @parametrize async def test_streaming_response_validate(self, async_client: AsyncGcore) -> None: @@ -101,6 +99,6 @@ async def test_streaming_response_validate(self, async_client: AsyncGcore) -> No assert response.http_request.headers.get("X-Stainless-Lang") == "python" custom_configuration = await response.parse() - assert_matches_type(CustomConfigurationValidateResponse, custom_configuration, path=["response"]) + assert_matches_type(PgConfValidation, custom_configuration, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/cloud/instances/test_images.py b/tests/api_resources/cloud/instances/test_images.py index ca1c50115..8368cc878 100644 --- a/tests/api_resources/cloud/instances/test_images.py +++ b/tests/api_resources/cloud/instances/test_images.py @@ -24,18 +24,18 @@ class TestImages: @parametrize def test_method_update(self, client: Gcore) -> None: image = client.cloud.instances.images.update( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert_matches_type(Image, image, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Gcore) -> None: image = client.cloud.instances.images.update( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, hw_firmware_type="bios", hw_machine_type="q35", is_baremetal=False, @@ -49,9 +49,9 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: @parametrize def test_raw_response_update(self, client: Gcore) -> None: response = client.cloud.instances.images.with_raw_response.update( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert response.is_closed is True @@ -62,9 +62,9 @@ def test_raw_response_update(self, client: Gcore) -> None: @parametrize def test_streaming_response_update(self, client: Gcore) -> None: with client.cloud.instances.images.with_streaming_response.update( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -79,8 +79,8 @@ def test_path_params_update(self, client: Gcore) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): client.cloud.instances.images.with_raw_response.update( image_id="", - project_id=0, - region_id=0, + project_id=1, + region_id=7, ) @parametrize @@ -133,18 +133,18 @@ def test_streaming_response_list(self, client: Gcore) -> None: @parametrize def test_method_delete(self, client: Gcore) -> None: image = client.cloud.instances.images.delete( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert_matches_type(TaskIDList, image, path=["response"]) @parametrize def test_raw_response_delete(self, client: Gcore) -> None: response = client.cloud.instances.images.with_raw_response.delete( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert response.is_closed is True @@ -155,9 +155,9 @@ def test_raw_response_delete(self, client: Gcore) -> None: @parametrize def test_streaming_response_delete(self, client: Gcore) -> None: with client.cloud.instances.images.with_streaming_response.delete( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -172,8 +172,8 @@ def test_path_params_delete(self, client: Gcore) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): client.cloud.instances.images.with_raw_response.delete( image_id="", - project_id=0, - region_id=0, + project_id=1, + region_id=7, ) @parametrize @@ -237,28 +237,28 @@ def test_streaming_response_create_from_volume(self, client: Gcore) -> None: @parametrize def test_method_get(self, client: Gcore) -> None: image = client.cloud.instances.images.get( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert_matches_type(Image, image, path=["response"]) @parametrize def test_method_get_with_all_params(self, client: Gcore) -> None: image = client.cloud.instances.images.get( - image_id="image_id", - project_id=0, - region_id=0, - include_prices=True, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + include_prices=False, ) assert_matches_type(Image, image, path=["response"]) @parametrize def test_raw_response_get(self, client: Gcore) -> None: response = client.cloud.instances.images.with_raw_response.get( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert response.is_closed is True @@ -269,9 +269,9 @@ def test_raw_response_get(self, client: Gcore) -> None: @parametrize def test_streaming_response_get(self, client: Gcore) -> None: with client.cloud.instances.images.with_streaming_response.get( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -286,15 +286,15 @@ def test_path_params_get(self, client: Gcore) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): client.cloud.instances.images.with_raw_response.get( image_id="", - project_id=0, - region_id=0, + project_id=1, + region_id=7, ) @parametrize def test_method_upload(self, client: Gcore) -> None: image = client.cloud.instances.images.upload( - project_id=0, - region_id=0, + project_id=1, + region_id=7, name="my-image", url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", ) @@ -303,8 +303,8 @@ def test_method_upload(self, client: Gcore) -> None: @parametrize def test_method_upload_with_all_params(self, client: Gcore) -> None: image = client.cloud.instances.images.upload( - project_id=0, - region_id=0, + project_id=1, + region_id=7, name="my-image", url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", architecture="x86_64", @@ -323,8 +323,8 @@ def test_method_upload_with_all_params(self, client: Gcore) -> None: @parametrize def test_raw_response_upload(self, client: Gcore) -> None: response = client.cloud.instances.images.with_raw_response.upload( - project_id=0, - region_id=0, + project_id=1, + region_id=7, name="my-image", url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", ) @@ -337,8 +337,8 @@ def test_raw_response_upload(self, client: Gcore) -> None: @parametrize def test_streaming_response_upload(self, client: Gcore) -> None: with client.cloud.instances.images.with_streaming_response.upload( - project_id=0, - region_id=0, + project_id=1, + region_id=7, name="my-image", url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", ) as response: @@ -359,18 +359,18 @@ class TestAsyncImages: @parametrize async def test_method_update(self, async_client: AsyncGcore) -> None: image = await async_client.cloud.instances.images.update( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert_matches_type(Image, image, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> None: image = await async_client.cloud.instances.images.update( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, hw_firmware_type="bios", hw_machine_type="q35", is_baremetal=False, @@ -384,9 +384,9 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> @parametrize async def test_raw_response_update(self, async_client: AsyncGcore) -> None: response = await async_client.cloud.instances.images.with_raw_response.update( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert response.is_closed is True @@ -397,9 +397,9 @@ async def test_raw_response_update(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_update(self, async_client: AsyncGcore) -> None: async with async_client.cloud.instances.images.with_streaming_response.update( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -414,8 +414,8 @@ async def test_path_params_update(self, async_client: AsyncGcore) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): await async_client.cloud.instances.images.with_raw_response.update( image_id="", - project_id=0, - region_id=0, + project_id=1, + region_id=7, ) @parametrize @@ -468,18 +468,18 @@ async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: @parametrize async def test_method_delete(self, async_client: AsyncGcore) -> None: image = await async_client.cloud.instances.images.delete( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert_matches_type(TaskIDList, image, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: response = await async_client.cloud.instances.images.with_raw_response.delete( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert response.is_closed is True @@ -490,9 +490,9 @@ async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncGcore) -> None: async with async_client.cloud.instances.images.with_streaming_response.delete( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -507,8 +507,8 @@ async def test_path_params_delete(self, async_client: AsyncGcore) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): await async_client.cloud.instances.images.with_raw_response.delete( image_id="", - project_id=0, - region_id=0, + project_id=1, + region_id=7, ) @parametrize @@ -572,28 +572,28 @@ async def test_streaming_response_create_from_volume(self, async_client: AsyncGc @parametrize async def test_method_get(self, async_client: AsyncGcore) -> None: image = await async_client.cloud.instances.images.get( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert_matches_type(Image, image, path=["response"]) @parametrize async def test_method_get_with_all_params(self, async_client: AsyncGcore) -> None: image = await async_client.cloud.instances.images.get( - image_id="image_id", - project_id=0, - region_id=0, - include_prices=True, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, + include_prices=False, ) assert_matches_type(Image, image, path=["response"]) @parametrize async def test_raw_response_get(self, async_client: AsyncGcore) -> None: response = await async_client.cloud.instances.images.with_raw_response.get( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) assert response.is_closed is True @@ -604,9 +604,9 @@ async def test_raw_response_get(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_get(self, async_client: AsyncGcore) -> None: async with async_client.cloud.instances.images.with_streaming_response.get( - image_id="image_id", - project_id=0, - region_id=0, + image_id="8cab6f28-09ca-4201-b3f7-23c7893f4bd6", + project_id=1, + region_id=7, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -621,15 +621,15 @@ async def test_path_params_get(self, async_client: AsyncGcore) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `image_id` but received ''"): await async_client.cloud.instances.images.with_raw_response.get( image_id="", - project_id=0, - region_id=0, + project_id=1, + region_id=7, ) @parametrize async def test_method_upload(self, async_client: AsyncGcore) -> None: image = await async_client.cloud.instances.images.upload( - project_id=0, - region_id=0, + project_id=1, + region_id=7, name="my-image", url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", ) @@ -638,8 +638,8 @@ async def test_method_upload(self, async_client: AsyncGcore) -> None: @parametrize async def test_method_upload_with_all_params(self, async_client: AsyncGcore) -> None: image = await async_client.cloud.instances.images.upload( - project_id=0, - region_id=0, + project_id=1, + region_id=7, name="my-image", url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", architecture="x86_64", @@ -658,8 +658,8 @@ async def test_method_upload_with_all_params(self, async_client: AsyncGcore) -> @parametrize async def test_raw_response_upload(self, async_client: AsyncGcore) -> None: response = await async_client.cloud.instances.images.with_raw_response.upload( - project_id=0, - region_id=0, + project_id=1, + region_id=7, name="my-image", url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", ) @@ -672,8 +672,8 @@ async def test_raw_response_upload(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_upload(self, async_client: AsyncGcore) -> None: async with async_client.cloud.instances.images.with_streaming_response.upload( - project_id=0, - region_id=0, + project_id=1, + region_id=7, name="my-image", url="http://mirror.noris.net/cirros/0.4.0/cirros-0.4.0-x86_64-disk.img", ) as response: diff --git a/tests/api_resources/cloud/networks/test_routers.py b/tests/api_resources/cloud/networks/test_routers.py index 942646ca7..460529d3e 100644 --- a/tests/api_resources/cloud/networks/test_routers.py +++ b/tests/api_resources/cloud/networks/test_routers.py @@ -15,8 +15,6 @@ Router, ) -# pyright: reportDeprecated=false - base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -87,77 +85,70 @@ def test_streaming_response_create(self, client: Gcore) -> None: @parametrize def test_method_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - router = client.cloud.networks.routers.update( - router_id="router_id", - project_id=0, - region_id=0, - ) - - assert_matches_type(Router, router, path=["response"]) + router = client.cloud.networks.routers.update( + router_id="ccd5b925-e35c-4611-a511-67ab503104c8", + project_id=1, + region_id=1, + ) + assert_matches_type(TaskIDList, router, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - router = client.cloud.networks.routers.update( - router_id="router_id", - project_id=0, - region_id=0, - external_gateway_info={ - "network_id": "d7745dcf-b302-4795-9d61-6cc52487af48", - "enable_snat": False, - "type": "manual", - }, - name="my_renamed_router", - routes=[ - { - "destination": "10.0.3.0/24", - "nexthop": "10.0.0.13", - } - ], - ) - - assert_matches_type(Router, router, path=["response"]) + router = client.cloud.networks.routers.update( + router_id="ccd5b925-e35c-4611-a511-67ab503104c8", + project_id=1, + region_id=1, + external_gateway_info={ + "network_id": "d7745dcf-b302-4795-9d61-6cc52487af48", + "enable_snat": False, + "type": "manual", + }, + name="my_renamed_router", + routes=[ + { + "destination": "10.0.3.0/24", + "nexthop": "10.0.0.13", + } + ], + ) + assert_matches_type(TaskIDList, router, path=["response"]) @parametrize def test_raw_response_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - response = client.cloud.networks.routers.with_raw_response.update( - router_id="router_id", - project_id=0, - region_id=0, - ) + response = client.cloud.networks.routers.with_raw_response.update( + router_id="ccd5b925-e35c-4611-a511-67ab503104c8", + project_id=1, + region_id=1, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" router = response.parse() - assert_matches_type(Router, router, path=["response"]) + assert_matches_type(TaskIDList, router, path=["response"]) @parametrize def test_streaming_response_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - with client.cloud.networks.routers.with_streaming_response.update( - router_id="router_id", - project_id=0, - region_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with client.cloud.networks.routers.with_streaming_response.update( + router_id="ccd5b925-e35c-4611-a511-67ab503104c8", + project_id=1, + region_id=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - router = response.parse() - assert_matches_type(Router, router, path=["response"]) + router = response.parse() + assert_matches_type(TaskIDList, router, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - with pytest.raises(ValueError, match=r"Expected a non-empty value for `router_id` but received ''"): - client.cloud.networks.routers.with_raw_response.update( - router_id="", - project_id=0, - region_id=0, - ) + with pytest.raises(ValueError, match=r"Expected a non-empty value for `router_id` but received ''"): + client.cloud.networks.routers.with_raw_response.update( + router_id="", + project_id=1, + region_id=1, + ) @parametrize def test_method_list(self, client: Gcore) -> None: @@ -477,77 +468,70 @@ async def test_streaming_response_create(self, async_client: AsyncGcore) -> None @parametrize async def test_method_update(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - router = await async_client.cloud.networks.routers.update( - router_id="router_id", - project_id=0, - region_id=0, - ) - - assert_matches_type(Router, router, path=["response"]) + router = await async_client.cloud.networks.routers.update( + router_id="ccd5b925-e35c-4611-a511-67ab503104c8", + project_id=1, + region_id=1, + ) + assert_matches_type(TaskIDList, router, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - router = await async_client.cloud.networks.routers.update( - router_id="router_id", - project_id=0, - region_id=0, - external_gateway_info={ - "network_id": "d7745dcf-b302-4795-9d61-6cc52487af48", - "enable_snat": False, - "type": "manual", - }, - name="my_renamed_router", - routes=[ - { - "destination": "10.0.3.0/24", - "nexthop": "10.0.0.13", - } - ], - ) - - assert_matches_type(Router, router, path=["response"]) + router = await async_client.cloud.networks.routers.update( + router_id="ccd5b925-e35c-4611-a511-67ab503104c8", + project_id=1, + region_id=1, + external_gateway_info={ + "network_id": "d7745dcf-b302-4795-9d61-6cc52487af48", + "enable_snat": False, + "type": "manual", + }, + name="my_renamed_router", + routes=[ + { + "destination": "10.0.3.0/24", + "nexthop": "10.0.0.13", + } + ], + ) + assert_matches_type(TaskIDList, router, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.cloud.networks.routers.with_raw_response.update( - router_id="router_id", - project_id=0, - region_id=0, - ) + response = await async_client.cloud.networks.routers.with_raw_response.update( + router_id="ccd5b925-e35c-4611-a511-67ab503104c8", + project_id=1, + region_id=1, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" router = await response.parse() - assert_matches_type(Router, router, path=["response"]) + assert_matches_type(TaskIDList, router, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.cloud.networks.routers.with_streaming_response.update( - router_id="router_id", - project_id=0, - region_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + async with async_client.cloud.networks.routers.with_streaming_response.update( + router_id="ccd5b925-e35c-4611-a511-67ab503104c8", + project_id=1, + region_id=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - router = await response.parse() - assert_matches_type(Router, router, path=["response"]) + router = await response.parse() + assert_matches_type(TaskIDList, router, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - with pytest.raises(ValueError, match=r"Expected a non-empty value for `router_id` but received ''"): - await async_client.cloud.networks.routers.with_raw_response.update( - router_id="", - project_id=0, - region_id=0, - ) + with pytest.raises(ValueError, match=r"Expected a non-empty value for `router_id` but received ''"): + await async_client.cloud.networks.routers.with_raw_response.update( + router_id="", + project_id=1, + region_id=1, + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/iam/test_api_tokens.py b/tests/api_resources/iam/test_api_tokens.py index fa911d108..314e99934 100644 --- a/tests/api_resources/iam/test_api_tokens.py +++ b/tests/api_resources/iam/test_api_tokens.py @@ -11,8 +11,6 @@ from tests.utils import assert_matches_type from gcore.types.iam import APIToken, APITokenList, APITokenCreated -# pyright: reportDeprecated=false - base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -21,43 +19,38 @@ class TestAPITokens: @parametrize def test_method_create(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = client.iam.api_tokens.create( - client_id=0, - client_user={}, - exp_date="2021-01-01T12:00:00.000000Z", - name="My token", - ) - + api_token = client.iam.api_tokens.create( + client_id=0, + client_user={}, + exp_date="2021-01-01T12:00:00.000000Z", + name="My token", + ) assert_matches_type(APITokenCreated, api_token, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = client.iam.api_tokens.create( - client_id=0, - client_user={ - "role": { - "id": 1, - "name": "Administrators", - } - }, - exp_date="2021-01-01T12:00:00.000000Z", - name="My token", - description="It's my token", - ) - + api_token = client.iam.api_tokens.create( + client_id=0, + client_user={ + "role": { + "id": 1, + "name": "Administrators", + } + }, + exp_date="2021-01-01T12:00:00.000000Z", + name="My token", + description="It's my token", + ) assert_matches_type(APITokenCreated, api_token, path=["response"]) @parametrize def test_raw_response_create(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - response = client.iam.api_tokens.with_raw_response.create( - client_id=0, - client_user={}, - exp_date="2021-01-01T12:00:00.000000Z", - name="My token", - ) + response = client.iam.api_tokens.with_raw_response.create( + client_id=0, + client_user={}, + exp_date="2021-01-01T12:00:00.000000Z", + name="My token", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -66,49 +59,43 @@ def test_raw_response_create(self, client: Gcore) -> None: @parametrize def test_streaming_response_create(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - with client.iam.api_tokens.with_streaming_response.create( - client_id=0, - client_user={}, - exp_date="2021-01-01T12:00:00.000000Z", - name="My token", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - api_token = response.parse() - assert_matches_type(APITokenCreated, api_token, path=["response"]) + with client.iam.api_tokens.with_streaming_response.create( + client_id=0, + client_user={}, + exp_date="2021-01-01T12:00:00.000000Z", + name="My token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = response.parse() + assert_matches_type(APITokenCreated, api_token, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_list(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = client.iam.api_tokens.list( - client_id=0, - ) - + api_token = client.iam.api_tokens.list( + client_id=0, + ) assert_matches_type(APITokenList, api_token, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = client.iam.api_tokens.list( - client_id=0, - deleted=True, - issued_by=0, - not_issued_by=0, - role="role", - ) - + api_token = client.iam.api_tokens.list( + client_id=0, + deleted=True, + issued_by=0, + not_issued_by=0, + role="role", + ) assert_matches_type(APITokenList, api_token, path=["response"]) @parametrize def test_raw_response_list(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - response = client.iam.api_tokens.with_raw_response.list( - client_id=0, - ) + response = client.iam.api_tokens.with_raw_response.list( + client_id=0, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -117,35 +104,31 @@ def test_raw_response_list(self, client: Gcore) -> None: @parametrize def test_streaming_response_list(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - with client.iam.api_tokens.with_streaming_response.list( - client_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with client.iam.api_tokens.with_streaming_response.list( + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - api_token = response.parse() - assert_matches_type(APITokenList, api_token, path=["response"]) + api_token = response.parse() + assert_matches_type(APITokenList, api_token, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = client.iam.api_tokens.delete( - token_id=0, - client_id=0, - ) - + api_token = client.iam.api_tokens.delete( + token_id=0, + client_id=0, + ) assert api_token is None @parametrize def test_raw_response_delete(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - response = client.iam.api_tokens.with_raw_response.delete( - token_id=0, - client_id=0, - ) + response = client.iam.api_tokens.with_raw_response.delete( + token_id=0, + client_id=0, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -154,36 +137,32 @@ def test_raw_response_delete(self, client: Gcore) -> None: @parametrize def test_streaming_response_delete(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - with client.iam.api_tokens.with_streaming_response.delete( - token_id=0, - client_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with client.iam.api_tokens.with_streaming_response.delete( + token_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - api_token = response.parse() - assert api_token is None + api_token = response.parse() + assert api_token is None assert cast(Any, response.is_closed) is True @parametrize def test_method_get(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = client.iam.api_tokens.get( - token_id=0, - client_id=0, - ) - + api_token = client.iam.api_tokens.get( + token_id=0, + client_id=0, + ) assert_matches_type(APIToken, api_token, path=["response"]) @parametrize def test_raw_response_get(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - response = client.iam.api_tokens.with_raw_response.get( - token_id=0, - client_id=0, - ) + response = client.iam.api_tokens.with_raw_response.get( + token_id=0, + client_id=0, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -192,16 +171,15 @@ def test_raw_response_get(self, client: Gcore) -> None: @parametrize def test_streaming_response_get(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - with client.iam.api_tokens.with_streaming_response.get( - token_id=0, - client_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with client.iam.api_tokens.with_streaming_response.get( + token_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - api_token = response.parse() - assert_matches_type(APIToken, api_token, path=["response"]) + api_token = response.parse() + assert_matches_type(APIToken, api_token, path=["response"]) assert cast(Any, response.is_closed) is True @@ -213,43 +191,38 @@ class TestAsyncAPITokens: @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = await async_client.iam.api_tokens.create( - client_id=0, - client_user={}, - exp_date="2021-01-01T12:00:00.000000Z", - name="My token", - ) - + api_token = await async_client.iam.api_tokens.create( + client_id=0, + client_user={}, + exp_date="2021-01-01T12:00:00.000000Z", + name="My token", + ) assert_matches_type(APITokenCreated, api_token, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = await async_client.iam.api_tokens.create( - client_id=0, - client_user={ - "role": { - "id": 1, - "name": "Administrators", - } - }, - exp_date="2021-01-01T12:00:00.000000Z", - name="My token", - description="It's my token", - ) - + api_token = await async_client.iam.api_tokens.create( + client_id=0, + client_user={ + "role": { + "id": 1, + "name": "Administrators", + } + }, + exp_date="2021-01-01T12:00:00.000000Z", + name="My token", + description="It's my token", + ) assert_matches_type(APITokenCreated, api_token, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.iam.api_tokens.with_raw_response.create( - client_id=0, - client_user={}, - exp_date="2021-01-01T12:00:00.000000Z", - name="My token", - ) + response = await async_client.iam.api_tokens.with_raw_response.create( + client_id=0, + client_user={}, + exp_date="2021-01-01T12:00:00.000000Z", + name="My token", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -258,49 +231,43 @@ async def test_raw_response_create(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.iam.api_tokens.with_streaming_response.create( - client_id=0, - client_user={}, - exp_date="2021-01-01T12:00:00.000000Z", - name="My token", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - api_token = await response.parse() - assert_matches_type(APITokenCreated, api_token, path=["response"]) + async with async_client.iam.api_tokens.with_streaming_response.create( + client_id=0, + client_user={}, + exp_date="2021-01-01T12:00:00.000000Z", + name="My token", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = await response.parse() + assert_matches_type(APITokenCreated, api_token, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = await async_client.iam.api_tokens.list( - client_id=0, - ) - + api_token = await async_client.iam.api_tokens.list( + client_id=0, + ) assert_matches_type(APITokenList, api_token, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = await async_client.iam.api_tokens.list( - client_id=0, - deleted=True, - issued_by=0, - not_issued_by=0, - role="role", - ) - + api_token = await async_client.iam.api_tokens.list( + client_id=0, + deleted=True, + issued_by=0, + not_issued_by=0, + role="role", + ) assert_matches_type(APITokenList, api_token, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.iam.api_tokens.with_raw_response.list( - client_id=0, - ) + response = await async_client.iam.api_tokens.with_raw_response.list( + client_id=0, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -309,35 +276,31 @@ async def test_raw_response_list(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.iam.api_tokens.with_streaming_response.list( - client_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + async with async_client.iam.api_tokens.with_streaming_response.list( + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - api_token = await response.parse() - assert_matches_type(APITokenList, api_token, path=["response"]) + api_token = await response.parse() + assert_matches_type(APITokenList, api_token, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = await async_client.iam.api_tokens.delete( - token_id=0, - client_id=0, - ) - + api_token = await async_client.iam.api_tokens.delete( + token_id=0, + client_id=0, + ) assert api_token is None @parametrize async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.iam.api_tokens.with_raw_response.delete( - token_id=0, - client_id=0, - ) + response = await async_client.iam.api_tokens.with_raw_response.delete( + token_id=0, + client_id=0, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -346,36 +309,32 @@ async def test_raw_response_delete(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_delete(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.iam.api_tokens.with_streaming_response.delete( - token_id=0, - client_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + async with async_client.iam.api_tokens.with_streaming_response.delete( + token_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - api_token = await response.parse() - assert api_token is None + api_token = await response.parse() + assert api_token is None assert cast(Any, response.is_closed) is True @parametrize async def test_method_get(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - api_token = await async_client.iam.api_tokens.get( - token_id=0, - client_id=0, - ) - + api_token = await async_client.iam.api_tokens.get( + token_id=0, + client_id=0, + ) assert_matches_type(APIToken, api_token, path=["response"]) @parametrize async def test_raw_response_get(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.iam.api_tokens.with_raw_response.get( - token_id=0, - client_id=0, - ) + response = await async_client.iam.api_tokens.with_raw_response.get( + token_id=0, + client_id=0, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -384,15 +343,14 @@ async def test_raw_response_get(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_get(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.iam.api_tokens.with_streaming_response.get( - token_id=0, - client_id=0, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - api_token = await response.parse() - assert_matches_type(APIToken, api_token, path=["response"]) + async with async_client.iam.api_tokens.with_streaming_response.get( + token_id=0, + client_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + api_token = await response.parse() + assert_matches_type(APIToken, api_token, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/iam/test_users.py b/tests/api_resources/iam/test_users.py index 64dd08a5c..9e63f4b51 100644 --- a/tests/api_resources/iam/test_users.py +++ b/tests/api_resources/iam/test_users.py @@ -22,7 +22,6 @@ class TestUsers: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="IMP-1903: OpenAPI spec PATCH requires PUT-only fields") @parametrize def test_method_update(self, client: Gcore) -> None: user = client.iam.users.update( @@ -42,7 +41,6 @@ def test_method_update_with_all_params(self, client: Gcore) -> None: ) assert_matches_type(User, user, path=["response"]) - @pytest.mark.skip(reason="IMP-1903: OpenAPI spec PATCH requires PUT-only fields") @parametrize def test_raw_response_update(self, client: Gcore) -> None: response = client.iam.users.with_raw_response.update( @@ -54,7 +52,6 @@ def test_raw_response_update(self, client: Gcore) -> None: user = response.parse() assert_matches_type(User, user, path=["response"]) - @pytest.mark.skip(reason="IMP-1903: OpenAPI spec PATCH requires PUT-only fields") @parametrize def test_streaming_response_update(self, client: Gcore) -> None: with client.iam.users.with_streaming_response.update( @@ -68,13 +65,13 @@ def test_streaming_response_update(self, client: Gcore) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="IMP-1904: OpenAPI spec missing required fields in response schema") + @pytest.mark.skip(reason="IMP-2027: PaginatedUsersArray.count is type: number in spec, should be integer") @parametrize def test_method_list(self, client: Gcore) -> None: user = client.iam.users.list() assert_matches_type(SyncOffsetPage[User], user, path=["response"]) - @pytest.mark.skip(reason="IMP-1904: OpenAPI spec missing required fields in response schema") + @pytest.mark.skip(reason="IMP-2027: PaginatedUsersArray.count is type: number in spec, should be integer") @parametrize def test_method_list_with_all_params(self, client: Gcore) -> None: user = client.iam.users.list( @@ -83,7 +80,7 @@ def test_method_list_with_all_params(self, client: Gcore) -> None: ) assert_matches_type(SyncOffsetPage[User], user, path=["response"]) - @pytest.mark.skip(reason="IMP-1904: OpenAPI spec missing required fields in response schema") + @pytest.mark.skip(reason="IMP-2027: PaginatedUsersArray.count is type: number in spec, should be integer") @parametrize def test_raw_response_list(self, client: Gcore) -> None: response = client.iam.users.with_raw_response.list() @@ -93,7 +90,7 @@ def test_raw_response_list(self, client: Gcore) -> None: user = response.parse() assert_matches_type(SyncOffsetPage[User], user, path=["response"]) - @pytest.mark.skip(reason="IMP-1904: OpenAPI spec missing required fields in response schema") + @pytest.mark.skip(reason="IMP-2027: PaginatedUsersArray.count is type: number in spec, should be integer") @parametrize def test_streaming_response_list(self, client: Gcore) -> None: with client.iam.users.with_streaming_response.list() as response: @@ -227,7 +224,6 @@ class TestAsyncUsers: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="IMP-1903: OpenAPI spec PATCH requires PUT-only fields") @parametrize async def test_method_update(self, async_client: AsyncGcore) -> None: user = await async_client.iam.users.update( @@ -247,7 +243,6 @@ async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> ) assert_matches_type(User, user, path=["response"]) - @pytest.mark.skip(reason="IMP-1903: OpenAPI spec PATCH requires PUT-only fields") @parametrize async def test_raw_response_update(self, async_client: AsyncGcore) -> None: response = await async_client.iam.users.with_raw_response.update( @@ -259,7 +254,6 @@ async def test_raw_response_update(self, async_client: AsyncGcore) -> None: user = await response.parse() assert_matches_type(User, user, path=["response"]) - @pytest.mark.skip(reason="IMP-1903: OpenAPI spec PATCH requires PUT-only fields") @parametrize async def test_streaming_response_update(self, async_client: AsyncGcore) -> None: async with async_client.iam.users.with_streaming_response.update( @@ -273,13 +267,13 @@ async def test_streaming_response_update(self, async_client: AsyncGcore) -> None assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="IMP-1904: OpenAPI spec missing required fields in response schema") + @pytest.mark.skip(reason="IMP-2027: PaginatedUsersArray.count is type: number in spec, should be integer") @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: user = await async_client.iam.users.list() assert_matches_type(AsyncOffsetPage[User], user, path=["response"]) - @pytest.mark.skip(reason="IMP-1904: OpenAPI spec missing required fields in response schema") + @pytest.mark.skip(reason="IMP-2027: PaginatedUsersArray.count is type: number in spec, should be integer") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> None: user = await async_client.iam.users.list( @@ -288,7 +282,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> No ) assert_matches_type(AsyncOffsetPage[User], user, path=["response"]) - @pytest.mark.skip(reason="IMP-1904: OpenAPI spec missing required fields in response schema") + @pytest.mark.skip(reason="IMP-2027: PaginatedUsersArray.count is type: number in spec, should be integer") @parametrize async def test_raw_response_list(self, async_client: AsyncGcore) -> None: response = await async_client.iam.users.with_raw_response.list() @@ -298,7 +292,7 @@ async def test_raw_response_list(self, async_client: AsyncGcore) -> None: user = await response.parse() assert_matches_type(AsyncOffsetPage[User], user, path=["response"]) - @pytest.mark.skip(reason="IMP-1904: OpenAPI spec missing required fields in response schema") + @pytest.mark.skip(reason="IMP-2027: PaginatedUsersArray.count is type: number in spec, should be integer") @parametrize async def test_streaming_response_list(self, async_client: AsyncGcore) -> None: async with async_client.iam.users.with_streaming_response.list() as response: diff --git a/tests/api_resources/storage/object_storages/test_access_keys.py b/tests/api_resources/storage/object_storages/test_access_keys.py index 97542d266..6ebd55f99 100644 --- a/tests/api_resources/storage/object_storages/test_access_keys.py +++ b/tests/api_resources/storage/object_storages/test_access_keys.py @@ -132,6 +132,48 @@ def test_path_params_delete(self, client: Gcore) -> None: storage_id=0, ) + @parametrize + def test_method_get(self, client: Gcore) -> None: + access_key = client.storage.object_storages.access_keys.get( + access_key="access_key", + storage_id=0, + ) + assert_matches_type(AccessKey, access_key, path=["response"]) + + @parametrize + def test_raw_response_get(self, client: Gcore) -> None: + response = client.storage.object_storages.access_keys.with_raw_response.get( + access_key="access_key", + storage_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + access_key = response.parse() + assert_matches_type(AccessKey, access_key, path=["response"]) + + @parametrize + def test_streaming_response_get(self, client: Gcore) -> None: + with client.storage.object_storages.access_keys.with_streaming_response.get( + access_key="access_key", + storage_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + access_key = response.parse() + assert_matches_type(AccessKey, access_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_get(self, client: Gcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `access_key` but received ''"): + client.storage.object_storages.access_keys.with_raw_response.get( + access_key="", + storage_id=0, + ) + class TestAsyncAccessKeys: parametrize = pytest.mark.parametrize( @@ -251,3 +293,45 @@ async def test_path_params_delete(self, async_client: AsyncGcore) -> None: access_key="", storage_id=0, ) + + @parametrize + async def test_method_get(self, async_client: AsyncGcore) -> None: + access_key = await async_client.storage.object_storages.access_keys.get( + access_key="access_key", + storage_id=0, + ) + assert_matches_type(AccessKey, access_key, path=["response"]) + + @parametrize + async def test_raw_response_get(self, async_client: AsyncGcore) -> None: + response = await async_client.storage.object_storages.access_keys.with_raw_response.get( + access_key="access_key", + storage_id=0, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + access_key = await response.parse() + assert_matches_type(AccessKey, access_key, path=["response"]) + + @parametrize + async def test_streaming_response_get(self, async_client: AsyncGcore) -> None: + async with async_client.storage.object_storages.access_keys.with_streaming_response.get( + access_key="access_key", + storage_id=0, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + access_key = await response.parse() + assert_matches_type(AccessKey, access_key, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_get(self, async_client: AsyncGcore) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `access_key` but received ''"): + await async_client.storage.object_storages.access_keys.with_raw_response.get( + access_key="", + storage_id=0, + ) diff --git a/tests/api_resources/storage/test_object_storages.py b/tests/api_resources/storage/test_object_storages.py index 3774cbd80..c6ea3db30 100644 --- a/tests/api_resources/storage/test_object_storages.py +++ b/tests/api_resources/storage/test_object_storages.py @@ -21,7 +21,7 @@ class TestObjectStorages: @parametrize def test_method_create(self, client: Gcore) -> None: object_storage = client.storage.object_storages.create( - location_name="s-ed1", + location_name="s-region-1", name="my-storage-prod", ) assert_matches_type(S3StorageCreated, object_storage, path=["response"]) @@ -29,7 +29,7 @@ def test_method_create(self, client: Gcore) -> None: @parametrize def test_raw_response_create(self, client: Gcore) -> None: response = client.storage.object_storages.with_raw_response.create( - location_name="s-ed1", + location_name="s-region-1", name="my-storage-prod", ) @@ -41,7 +41,7 @@ def test_raw_response_create(self, client: Gcore) -> None: @parametrize def test_streaming_response_create(self, client: Gcore) -> None: with client.storage.object_storages.with_streaming_response.create( - location_name="s-ed1", + location_name="s-region-1", name="my-storage-prod", ) as response: assert not response.is_closed @@ -193,7 +193,7 @@ class TestAsyncObjectStorages: @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: object_storage = await async_client.storage.object_storages.create( - location_name="s-ed1", + location_name="s-region-1", name="my-storage-prod", ) assert_matches_type(S3StorageCreated, object_storage, path=["response"]) @@ -201,7 +201,7 @@ async def test_method_create(self, async_client: AsyncGcore) -> None: @parametrize async def test_raw_response_create(self, async_client: AsyncGcore) -> None: response = await async_client.storage.object_storages.with_raw_response.create( - location_name="s-ed1", + location_name="s-region-1", name="my-storage-prod", ) @@ -213,7 +213,7 @@ async def test_raw_response_create(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncGcore) -> None: async with async_client.storage.object_storages.with_streaming_response.create( - location_name="s-ed1", + location_name="s-region-1", name="my-storage-prod", ) as response: assert not response.is_closed diff --git a/tests/api_resources/storage/test_sftp_storages.py b/tests/api_resources/storage/test_sftp_storages.py index 120a2878e..ffb70efb9 100644 --- a/tests/api_resources/storage/test_sftp_storages.py +++ b/tests/api_resources/storage/test_sftp_storages.py @@ -23,7 +23,7 @@ class TestSftpStorages: @parametrize def test_method_create(self, client: Gcore) -> None: sftp_storage = client.storage.sftp_storages.create( - location_name="mia", + location_name="s-region-1", name="my-sftp-storage", password_mode="auto", ) @@ -32,7 +32,7 @@ def test_method_create(self, client: Gcore) -> None: @parametrize def test_method_create_with_all_params(self, client: Gcore) -> None: sftp_storage = client.storage.sftp_storages.create( - location_name="mia", + location_name="s-region-1", name="my-sftp-storage", password_mode="auto", expires="2 years 6 months", @@ -47,7 +47,7 @@ def test_method_create_with_all_params(self, client: Gcore) -> None: @parametrize def test_raw_response_create(self, client: Gcore) -> None: response = client.storage.sftp_storages.with_raw_response.create( - location_name="mia", + location_name="s-region-1", name="my-sftp-storage", password_mode="auto", ) @@ -60,7 +60,7 @@ def test_raw_response_create(self, client: Gcore) -> None: @parametrize def test_streaming_response_create(self, client: Gcore) -> None: with client.storage.sftp_storages.with_streaming_response.create( - location_name="mia", + location_name="s-region-1", name="my-sftp-storage", password_mode="auto", ) as response: @@ -226,7 +226,7 @@ class TestAsyncSftpStorages: @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: sftp_storage = await async_client.storage.sftp_storages.create( - location_name="mia", + location_name="s-region-1", name="my-sftp-storage", password_mode="auto", ) @@ -235,7 +235,7 @@ async def test_method_create(self, async_client: AsyncGcore) -> None: @parametrize async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> None: sftp_storage = await async_client.storage.sftp_storages.create( - location_name="mia", + location_name="s-region-1", name="my-sftp-storage", password_mode="auto", expires="2 years 6 months", @@ -250,7 +250,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> @parametrize async def test_raw_response_create(self, async_client: AsyncGcore) -> None: response = await async_client.storage.sftp_storages.with_raw_response.create( - location_name="mia", + location_name="s-region-1", name="my-sftp-storage", password_mode="auto", ) @@ -263,7 +263,7 @@ async def test_raw_response_create(self, async_client: AsyncGcore) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncGcore) -> None: async with async_client.storage.sftp_storages.with_streaming_response.create( - location_name="mia", + location_name="s-region-1", name="my-sftp-storage", password_mode="auto", ) as response: diff --git a/tests/api_resources/waap/test_analytics.py b/tests/api_resources/waap/test_analytics.py index 7318725ac..adae66f57 100644 --- a/tests/api_resources/waap/test_analytics.py +++ b/tests/api_resources/waap/test_analytics.py @@ -84,6 +84,12 @@ def test_method_get_requests_with_all_params(self, client: Gcore) -> None: decision=["allowed", "blocked"], domains=[1, 2, 3], end="2024-04-14T12:00:00Z", + exclude_countries=["DE", "MY"], + exclude_domains=[0], + exclude_ips=["1.2.3.4", "2001:678:194::3c25:ddad"], + exclude_reference_ids=["210b9798eb53baa4e69d31c1071cf03d"], + exclude_security_rule_names=["SQL injection"], + exclude_session_ids=["210b9798eb53baa4e69d31c1071cf03d"], http_methods=["GET", "HEAD"], ips=["1.2.3.4", " 2001:678:194::3c25:ddad"], limit=0, @@ -136,6 +142,7 @@ def test_method_get_traffic_with_all_params(self, client: Gcore) -> None: analytics = client.waap.analytics.get_traffic( resolution="daily", start="2024-04-13T00:00:00+01:00", + bucket_size=60, domains=[1, 2, 3], end="2024-04-14T12:00:00Z", ) @@ -180,10 +187,17 @@ def test_method_get_traffic_filtered_with_all_params(self, client: Gcore) -> Non analytics = client.waap.analytics.get_traffic_filtered( resolution="daily", start="2024-04-13T00:00:00+01:00", + bucket_size=60, countries=["DE", "MY"], decision=["allowed", "blocked"], domains=[1, 2, 3], end="2024-04-14T12:00:00Z", + exclude_countries=["DE", "MY"], + exclude_domains=[0], + exclude_ips=["1.2.3.4", "2001:678:194::3c25:ddad"], + exclude_reference_ids=["210b9798eb53baa4e69d31c1071cf03d"], + exclude_security_rule_names=["SQL injection"], + exclude_session_ids=["210b9798eb53baa4e69d31c1071cf03d"], http_methods=["GET", "HEAD"], ips=["1.2.3.4", " 2001:678:194::3c25:ddad"], optional_action=["captcha", "challenge"], @@ -289,6 +303,12 @@ async def test_method_get_requests_with_all_params(self, async_client: AsyncGcor decision=["allowed", "blocked"], domains=[1, 2, 3], end="2024-04-14T12:00:00Z", + exclude_countries=["DE", "MY"], + exclude_domains=[0], + exclude_ips=["1.2.3.4", "2001:678:194::3c25:ddad"], + exclude_reference_ids=["210b9798eb53baa4e69d31c1071cf03d"], + exclude_security_rule_names=["SQL injection"], + exclude_session_ids=["210b9798eb53baa4e69d31c1071cf03d"], http_methods=["GET", "HEAD"], ips=["1.2.3.4", " 2001:678:194::3c25:ddad"], limit=0, @@ -341,6 +361,7 @@ async def test_method_get_traffic_with_all_params(self, async_client: AsyncGcore analytics = await async_client.waap.analytics.get_traffic( resolution="daily", start="2024-04-13T00:00:00+01:00", + bucket_size=60, domains=[1, 2, 3], end="2024-04-14T12:00:00Z", ) @@ -385,10 +406,17 @@ async def test_method_get_traffic_filtered_with_all_params(self, async_client: A analytics = await async_client.waap.analytics.get_traffic_filtered( resolution="daily", start="2024-04-13T00:00:00+01:00", + bucket_size=60, countries=["DE", "MY"], decision=["allowed", "blocked"], domains=[1, 2, 3], end="2024-04-14T12:00:00Z", + exclude_countries=["DE", "MY"], + exclude_domains=[0], + exclude_ips=["1.2.3.4", "2001:678:194::3c25:ddad"], + exclude_reference_ids=["210b9798eb53baa4e69d31c1071cf03d"], + exclude_security_rule_names=["SQL injection"], + exclude_session_ids=["210b9798eb53baa4e69d31c1071cf03d"], http_methods=["GET", "HEAD"], ips=["1.2.3.4", " 2001:678:194::3c25:ddad"], optional_action=["captcha", "challenge"], diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index e05e48bdc..771a511a9 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -4,7 +4,7 @@ import pytest -from gcore._types import FileTypes +from gcore._types import FileTypes, ArrayFormat from gcore._utils import extract_files @@ -37,10 +37,7 @@ def test_multiple_files() -> None: def test_top_level_file_array() -> None: query = {"files": [b"file one", b"file two"], "title": "hello"} - assert extract_files(query, paths=[["files", ""]]) == [ - ("files[]", b"file one"), - ("files[]", b"file two"), - ] + assert extract_files(query, paths=[["files", ""]]) == [("files[]", b"file one"), ("files[]", b"file two")] assert query == {"title": "hello"} @@ -71,3 +68,24 @@ def test_ignores_incorrect_paths( expected: list[tuple[str, FileTypes]], ) -> None: assert extract_files(query, paths=paths) == expected + + +@pytest.mark.parametrize( + "array_format,expected_top_level,expected_nested", + [ + ("brackets", [("files[]", b"a"), ("files[]", b"b")], [("items[][file]", b"a"), ("items[][file]", b"b")]), + ("repeat", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("comma", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("indices", [("files[0]", b"a"), ("files[1]", b"b")], [("items[0][file]", b"a"), ("items[1][file]", b"b")]), + ], +) +def test_array_format_controls_file_field_names( + array_format: ArrayFormat, + expected_top_level: list[tuple[str, FileTypes]], + expected_nested: list[tuple[str, FileTypes]], +) -> None: + top_level = {"files": [b"a", b"b"]} + assert extract_files(top_level, paths=[["files", ""]], array_format=array_format) == expected_top_level + + nested = {"items": [{"file": b"a"}, {"file": b"b"}]} + assert extract_files(nested, paths=[["items", "", "file"]], array_format=array_format) == expected_nested diff --git a/tests/test_files.py b/tests/test_files.py index 054b0adf5..b044139b7 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -131,7 +131,7 @@ def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: copied = deepcopy_with_paths(original, [["items", "", "file"]]) extracted = extract_files(copied, paths=[["items", "", "file"]]) - assert extracted == [("items[][file]", file1), ("items[][file]", file2)] + assert [entry for _, entry in extracted] == [file1, file2] assert original == { "items": [ {"file": file1, "extra": 1},