diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b8dda9bf..554e34bb 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.29.0" + ".": "0.30.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 70d5bc3d..23a96e2a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 645 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-3fc43b72b82321f8d4ceea9ea44d7ab14e75872dbe66e3e698e1f59ba300ec55.yml openapi_spec_hash: 1b1043a0ef7bcf106abf14f9187f5755 -config_hash: 7085751e6bd8f3fd13cfebe04bb99fed +config_hash: da497b83ee3cacf93b7f67fc7d5b1164 diff --git a/CHANGELOG.md b/CHANGELOG.md index e4b5a07b..7cba4462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.30.0 (2026-01-20) + +Full Changelog: [v0.29.0...v0.30.0](https://github.com/G-Core/gcore-python/compare/v0.29.0...v0.30.0) + +### ⚠ BREAKING CHANGES + +* **cloud:** use create and update v2 endpoints for security groups +* **cloud:** use v2 endpoint for floating IPs updates + +### Features + +* **cloud:** add create_and_poll and update_and_poll methods for security groups ([a0f8a75](https://github.com/G-Core/gcore-python/commit/a0f8a759796951e084b318e91a95ba413fa3b349)) +* **cloud:** add update_and_poll method for floating IPs ([26bfe18](https://github.com/G-Core/gcore-python/commit/26bfe184ecda853f88b7ba13f4eb4f2153237a7b)) +* **cloud:** use create and update v2 endpoints for security groups ([31501d3](https://github.com/G-Core/gcore-python/commit/31501d36608339851da4b8982bb32bf18a7518cb)) +* **cloud:** use v2 endpoint for floating IPs updates ([31ef098](https://github.com/G-Core/gcore-python/commit/31ef0984768bca7f1aa8b2fa5eb8249fca7af36f)) + ## 0.29.0 (2026-01-19) Full Changelog: [v0.28.0...v0.29.0](https://github.com/G-Core/gcore-python/compare/v0.28.0...v0.29.0) diff --git a/api.md b/api.md index a877e26b..4184baed 100644 --- a/api.md +++ b/api.md @@ -414,7 +414,7 @@ from gcore.types.cloud import FloatingIPDetailed Methods: - client.cloud.floating_ips.create(\*, project_id, region_id, \*\*params) -> TaskIDList -- client.cloud.floating_ips.update(floating_ip_id, \*, project_id, region_id, \*\*params) -> FloatingIP +- client.cloud.floating_ips.update(floating_ip_id, \*, project_id, region_id, \*\*params) -> TaskIDList - client.cloud.floating_ips.list(\*, project_id, region_id, \*\*params) -> SyncOffsetPage[FloatingIPDetailed] - client.cloud.floating_ips.delete(floating_ip_id, \*, project_id, region_id) -> TaskIDList - client.cloud.floating_ips.assign(floating_ip_id, \*, project_id, region_id, \*\*params) -> FloatingIP @@ -431,8 +431,8 @@ from gcore.types.cloud import SecurityGroup, SecurityGroupRule Methods: -- client.cloud.security_groups.create(\*, project_id, region_id, \*\*params) -> SecurityGroup -- client.cloud.security_groups.update(group_id, \*, project_id, region_id, \*\*params) -> SecurityGroup +- client.cloud.security_groups.create(\*, project_id, region_id, \*\*params) -> TaskIDList +- client.cloud.security_groups.update(group_id, \*, project_id, region_id, \*\*params) -> TaskIDList - client.cloud.security_groups.list(\*, project_id, region_id, \*\*params) -> SyncOffsetPage[SecurityGroup] - client.cloud.security_groups.delete(group_id, \*, project_id, region_id) -> None - client.cloud.security_groups.copy(group_id, \*, project_id, region_id, \*\*params) -> SecurityGroup diff --git a/examples/cloud/floating_ips.py b/examples/cloud/floating_ips.py index a2b44c48..8cf9ba33 100644 --- a/examples/cloud/floating_ips.py +++ b/examples/cloud/floating_ips.py @@ -26,6 +26,7 @@ def main() -> None: floating_ip_id = create_floating_ip(client=gcore) list_floating_ips(client=gcore) get_floating_ip(client=gcore, floating_ip_id=floating_ip_id) + update_tags_floating_ip(client=gcore, floating_ip_id=floating_ip_id) assign_floating_ip(client=gcore, floating_ip_id=floating_ip_id, port_id=cloud_port_id) unassign_floating_ip(client=gcore, floating_ip_id=floating_ip_id) delete_floating_ip(client=gcore, floating_ip_id=floating_ip_id) @@ -33,7 +34,7 @@ def main() -> None: def create_floating_ip(*, client: Gcore) -> str: print("\n=== CREATE FLOATING IP ===") - floating_ip = client.cloud.floating_ips.create_and_poll(tags={"name": "gcore-go-example"}) + floating_ip = client.cloud.floating_ips.create_and_poll(tags={"name": "gcore-gython-example"}) print(f"Created Floating IP: ID={floating_ip.id}") print("========================") return floating_ip.id @@ -58,9 +59,19 @@ def get_floating_ip(*, client: Gcore, floating_ip_id: str) -> None: print("========================") +def update_tags_floating_ip(*, client: Gcore, floating_ip_id: str) -> None: + print("\n=== UPDATE TAGS FLOATING IP ===") + floating_ip = client.cloud.floating_ips.update_and_poll( + floating_ip_id=floating_ip_id, + tags={"environment": "production", "team": "backend"}, + ) + print(f"Updated floating IP tags: ID={floating_ip.id}, tags={floating_ip.tags}") + print("========================") + + def assign_floating_ip(*, client: Gcore, floating_ip_id: str, port_id: str) -> None: print("\n=== ASSIGN FLOATING IP ===") - floating_ip = client.cloud.floating_ips.assign( # pyright: ignore[reportDeprecated] + floating_ip = client.cloud.floating_ips.update_and_poll( floating_ip_id=floating_ip_id, port_id=port_id, ) @@ -70,7 +81,10 @@ def assign_floating_ip(*, client: Gcore, floating_ip_id: str, port_id: str) -> N def unassign_floating_ip(*, client: Gcore, floating_ip_id: str) -> None: print("\n=== UNASSIGN FLOATING IP ===") - floating_ip = client.cloud.floating_ips.unassign(floating_ip_id=floating_ip_id) # pyright: ignore[reportDeprecated] + floating_ip = client.cloud.floating_ips.update_and_poll( + floating_ip_id=floating_ip_id, + port_id=None, + ) print(f"Unassigned floating IP: ID={floating_ip.id}") print("========================") diff --git a/examples/cloud/floating_ips_async.py b/examples/cloud/floating_ips_async.py index aea3370e..7fc4d467 100644 --- a/examples/cloud/floating_ips_async.py +++ b/examples/cloud/floating_ips_async.py @@ -27,6 +27,7 @@ async def main() -> None: floating_ip_id = await create_floating_ip(client=gcore) await list_floating_ips(client=gcore) await get_floating_ip(client=gcore, floating_ip_id=floating_ip_id) + await update_tags_floating_ip(client=gcore, floating_ip_id=floating_ip_id) await assign_floating_ip(client=gcore, floating_ip_id=floating_ip_id, port_id=cloud_port_id) await unassign_floating_ip(client=gcore, floating_ip_id=floating_ip_id) await delete_floating_ip(client=gcore, floating_ip_id=floating_ip_id) @@ -34,7 +35,7 @@ async def main() -> None: async def create_floating_ip(*, client: AsyncGcore) -> str: print("\n=== CREATE FLOATING IP ===") - floating_ip = await client.cloud.floating_ips.create_and_poll(tags={"name": "gcore-go-example"}) + floating_ip = await client.cloud.floating_ips.create_and_poll(tags={"name": "gcore-python-example"}) print(f"Created floating IP: ID={floating_ip.id}") print("========================") return floating_ip.id @@ -58,9 +59,19 @@ async def get_floating_ip(*, client: AsyncGcore, floating_ip_id: str) -> None: print("========================") +async def update_tags_floating_ip(*, client: AsyncGcore, floating_ip_id: str) -> None: + print("\n=== UPDATE TAGS FLOATING IP ===") + floating_ip = await client.cloud.floating_ips.update_and_poll( + floating_ip_id=floating_ip_id, + tags={"environment": "production", "team": "backend"}, + ) + print(f"Updated floating IP tags: ID={floating_ip.id}, tags={floating_ip.tags}") + print("========================") + + async def assign_floating_ip(*, client: AsyncGcore, floating_ip_id: str, port_id: str) -> None: print("\n=== ASSIGN FLOATING IP ===") - floating_ip = await client.cloud.floating_ips.assign( # pyright: ignore[reportDeprecated] + floating_ip = await client.cloud.floating_ips.update_and_poll( floating_ip_id=floating_ip_id, port_id=port_id, ) @@ -70,7 +81,10 @@ async def assign_floating_ip(*, client: AsyncGcore, floating_ip_id: str, port_id async def unassign_floating_ip(*, client: AsyncGcore, floating_ip_id: str) -> None: print("\n=== UNASSIGN FLOATING IP ===") - floating_ip = await client.cloud.floating_ips.unassign(floating_ip_id=floating_ip_id) # pyright: ignore[reportDeprecated] + floating_ip = await client.cloud.floating_ips.update_and_poll( + floating_ip_id=floating_ip_id, + port_id=None, + ) print(f"Unassigned floating IP: ID={floating_ip.id}") print("========================") diff --git a/examples/cloud/security_groups.py b/examples/cloud/security_groups.py index 9d46c6df..6188d57e 100644 --- a/examples/cloud/security_groups.py +++ b/examples/cloud/security_groups.py @@ -1,5 +1,4 @@ from gcore import Gcore -from gcore.types.cloud.security_group_create_params import SecurityGroup def main() -> None: @@ -20,6 +19,7 @@ def main() -> None: security_group_id = create_security_group(client=gcore) list_security_groups(client=gcore) get_security_group(client=gcore, security_group_id=security_group_id) + update_tags_security_group(client=gcore, security_group_id=security_group_id) update_security_group(client=gcore, security_group_id=security_group_id) # Rules @@ -32,7 +32,10 @@ def main() -> None: def create_security_group(*, client: Gcore) -> str: print("\n=== CREATE SECURITY GROUP ===") - security_group = client.cloud.security_groups.create(security_group=SecurityGroup(name="gcore-go-example")) # pyright: ignore[reportDeprecated] + security_group = client.cloud.security_groups.create_and_poll( + name="gcore-python-example", + tags={"environment": "development"}, + ) print(f"Created security group: ID={security_group.id}, name={security_group.name}") print("========================") return security_group.id @@ -55,11 +58,21 @@ def get_security_group(*, client: Gcore, security_group_id: str) -> None: print("========================") +def update_tags_security_group(*, client: Gcore, security_group_id: str) -> None: + print("\n=== UPDATE TAGS SECURITY GROUP ===") + security_group = client.cloud.security_groups.update_and_poll( + group_id=security_group_id, + tags={"environment": "production", "team": "backend"}, + ) + print(f"Updated security group tags: ID={security_group.id}, tags={security_group.tags_v2}") + print("========================") + + def update_security_group(*, client: Gcore, security_group_id: str) -> None: print("\n=== UPDATE SECURITY GROUP ===") - security_group = client.cloud.security_groups.update( # pyright: ignore[reportDeprecated] + security_group = client.cloud.security_groups.update_and_poll( group_id=security_group_id, - name="gcore-go-example-updated", + name="gcore-python-example-updated", ) print(f"Updated security group: ID={security_group.id}, name={security_group.name}") print("========================") diff --git a/examples/cloud/security_groups_async.py b/examples/cloud/security_groups_async.py index 543cba84..29ed42ad 100644 --- a/examples/cloud/security_groups_async.py +++ b/examples/cloud/security_groups_async.py @@ -1,7 +1,6 @@ import asyncio from gcore import AsyncGcore -from gcore.types.cloud.security_group_create_params import SecurityGroup async def main() -> None: @@ -22,6 +21,7 @@ async def main() -> None: security_group_id = await create_security_group(client=gcore) await list_security_groups(client=gcore) await get_security_group(client=gcore, security_group_id=security_group_id) + await update_tags_security_group(client=gcore, security_group_id=security_group_id) await update_security_group(client=gcore, security_group_id=security_group_id) # Rules @@ -34,7 +34,10 @@ async def main() -> None: async def create_security_group(client: AsyncGcore) -> str: print("\n=== CREATE SECURITY GROUP ===") - security_group = await client.cloud.security_groups.create(security_group=SecurityGroup(name="gcore-go-example")) # pyright: ignore[reportDeprecated] + security_group = await client.cloud.security_groups.create_and_poll( + name="gcore-python-example", + tags={"environment": "development"}, + ) print(f"Created security group: ID={security_group.id}, name={security_group.name}") print("========================") return security_group.id @@ -42,9 +45,8 @@ async def create_security_group(client: AsyncGcore) -> str: async def list_security_groups(*, client: AsyncGcore) -> None: print("\n=== LIST SECURITY GROUPS ===") - security_groups = await client.cloud.security_groups.list() count = 0 - async for security_group in security_groups: + async for security_group in client.cloud.security_groups.list(): count += 1 print(f"{count}. Security group: ID={security_group.id}, name={security_group.name}") print("========================") @@ -59,11 +61,21 @@ async def get_security_group(*, client: AsyncGcore, security_group_id: str) -> N print("========================") +async def update_tags_security_group(*, client: AsyncGcore, security_group_id: str) -> None: + print("\n=== UPDATE TAGS SECURITY GROUP ===") + security_group = await client.cloud.security_groups.update_and_poll( + group_id=security_group_id, + tags={"environment": "production", "team": "backend"}, + ) + print(f"Updated security group tags: ID={security_group.id}, tags={security_group.tags_v2}") + print("========================") + + async def update_security_group(*, client: AsyncGcore, security_group_id: str) -> None: print("\n=== UPDATE SECURITY GROUP ===") - security_group = await client.cloud.security_groups.update( # pyright: ignore[reportDeprecated] + security_group = await client.cloud.security_groups.update_and_poll( group_id=security_group_id, - name="gcore-go-example-updated", + name="gcore-python-example-updated", ) print(f"Updated security group: ID={security_group.id}, name={security_group.name}") print("========================") diff --git a/pyproject.toml b/pyproject.toml index 89661fbe..26af6423 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "gcore" -version = "0.29.0" +version = "0.30.0" description = "The official Python library for the gcore API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/gcore/_version.py b/src/gcore/_version.py index 606d0626..574d5dd3 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.29.0" # x-release-please-version +__version__ = "0.30.0" # x-release-please-version diff --git a/src/gcore/resources/cloud/floating_ips.py b/src/gcore/resources/cloud/floating_ips.py index a2cfd223..87243f25 100644 --- a/src/gcore/resources/cloud/floating_ips.py +++ b/src/gcore/resources/cloud/floating_ips.py @@ -124,13 +124,14 @@ def create( cast_to=TaskIDList, ) - @typing_extensions.deprecated("deprecated") def update( self, floating_ip_id: str, *, project_id: int | None = None, region_id: int | None = None, + fixed_ip_address: Optional[str] | Omit = omit, + port_id: Optional[str] | Omit = omit, tags: Optional[TagUpdateMapParam] | 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. @@ -138,10 +139,52 @@ def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> FloatingIP: - """ - **Deprecated**: Use PATCH - /v2/floatingips/{`project_id`}/{`region_id`}/{`floating_ip_id`} instead + ) -> TaskIDList: + """This endpoint updates the association and tags of an existing Floating IP. + + The + behavior depends on the current association state and the provided fields: + + Parameters: + + `port_id`: The unique identifier of the network interface (port) to which the + Floating IP should be assigned. This ID can be retrieved from the "Get instance" + or "List network interfaces" endpoints. + + `fixed_ip_address`: The private IP address assigned to the network interface. + This must be one of the IP addresses currently assigned to the specified port. + You can retrieve available fixed IP addresses from the "Get instance" or "List + network interfaces" endpoints. + + When the Floating IP has no port associated (`port_id` is null): + + - Patch with both `port_id` and `fixed_ip_address`: Assign the Floating IP to + the specified port and the provided `fixed_ip_address`, if that + `fixed_ip_address` exists on the port and is not yet used by another Floating + IP. + - Patch with `port_id` only (`fixed_ip_address` omitted): Assign the Floating IP + to the specified port using the first available IPv4 fixed IP of that port. + + When the Floating IP is already associated with a port: + + - Patch with both `port_id` and `fixed_ip_address`: Re-assign the Floating IP to + the specified port and address if all validations pass. + - Patch with `port_id` only (`fixed_ip_address` omitted): Re-assign the Floating + IP to the specified port using the first available IPv4 fixed IP of that port. + - Patch with `port_id` = null: Unassign the Floating IP from its current port. + + Tags: + + - You can update tags alongside association changes. Tags are provided as a list + of key-value pairs. + + Idempotency and task creation: + + - No worker task is created if the requested state is already actual, i.e., the + requested `port_id` equals the current `port_id` and/or the requested + `fixed_ip_address` equals the current `fixed_ip_address`, and the tags already + match the current tags. In such cases, the endpoint returns an empty tasks + list. Args: project_id: Project ID @@ -150,6 +193,10 @@ def update( floating_ip_id: Floating IP ID + fixed_ip_address: Fixed IP address + + port_id: Port ID + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. Unspecified tags remain unchanged. Read-only tags are always preserved and @@ -192,12 +239,19 @@ def update( if not floating_ip_id: raise ValueError(f"Expected a non-empty value for `floating_ip_id` but received {floating_ip_id!r}") return self._patch( - f"/cloud/v1/floatingips/{project_id}/{region_id}/{floating_ip_id}", - body=maybe_transform({"tags": tags}, floating_ip_update_params.FloatingIPUpdateParams), + f"/cloud/v2/floatingips/{project_id}/{region_id}/{floating_ip_id}", + body=maybe_transform( + { + "fixed_ip_address": fixed_ip_address, + "port_id": port_id, + "tags": tags, + }, + floating_ip_update_params.FloatingIPUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=FloatingIP, + cast_to=TaskIDList, ) def list( @@ -522,6 +576,56 @@ def create_and_poll( timeout=timeout, ) + def update_and_poll( + self, + floating_ip_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + fixed_ip_address: Optional[str] | Omit = omit, + port_id: Optional[str] | Omit = omit, + tags: Optional[TagUpdateMapParam] | 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, + ) -> FloatingIP: + """ + Update floating IP 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( + floating_ip_id=floating_ip_id, + project_id=project_id, + region_id=region_id, + fixed_ip_address=fixed_ip_address, + port_id=port_id, + tags=tags, + 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( + floating_ip_id=floating_ip_id, + project_id=project_id, + region_id=region_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + def delete_and_poll( self, floating_ip_id: str, @@ -648,13 +752,14 @@ async def create( cast_to=TaskIDList, ) - @typing_extensions.deprecated("deprecated") async def update( self, floating_ip_id: str, *, project_id: int | None = None, region_id: int | None = None, + fixed_ip_address: Optional[str] | Omit = omit, + port_id: Optional[str] | Omit = omit, tags: Optional[TagUpdateMapParam] | 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. @@ -662,10 +767,52 @@ async def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> FloatingIP: - """ - **Deprecated**: Use PATCH - /v2/floatingips/{`project_id`}/{`region_id`}/{`floating_ip_id`} instead + ) -> TaskIDList: + """This endpoint updates the association and tags of an existing Floating IP. + + The + behavior depends on the current association state and the provided fields: + + Parameters: + + `port_id`: The unique identifier of the network interface (port) to which the + Floating IP should be assigned. This ID can be retrieved from the "Get instance" + or "List network interfaces" endpoints. + + `fixed_ip_address`: The private IP address assigned to the network interface. + This must be one of the IP addresses currently assigned to the specified port. + You can retrieve available fixed IP addresses from the "Get instance" or "List + network interfaces" endpoints. + + When the Floating IP has no port associated (`port_id` is null): + + - Patch with both `port_id` and `fixed_ip_address`: Assign the Floating IP to + the specified port and the provided `fixed_ip_address`, if that + `fixed_ip_address` exists on the port and is not yet used by another Floating + IP. + - Patch with `port_id` only (`fixed_ip_address` omitted): Assign the Floating IP + to the specified port using the first available IPv4 fixed IP of that port. + + When the Floating IP is already associated with a port: + + - Patch with both `port_id` and `fixed_ip_address`: Re-assign the Floating IP to + the specified port and address if all validations pass. + - Patch with `port_id` only (`fixed_ip_address` omitted): Re-assign the Floating + IP to the specified port using the first available IPv4 fixed IP of that port. + - Patch with `port_id` = null: Unassign the Floating IP from its current port. + + Tags: + + - You can update tags alongside association changes. Tags are provided as a list + of key-value pairs. + + Idempotency and task creation: + + - No worker task is created if the requested state is already actual, i.e., the + requested `port_id` equals the current `port_id` and/or the requested + `fixed_ip_address` equals the current `fixed_ip_address`, and the tags already + match the current tags. In such cases, the endpoint returns an empty tasks + list. Args: project_id: Project ID @@ -674,6 +821,10 @@ async def update( floating_ip_id: Floating IP ID + fixed_ip_address: Fixed IP address + + port_id: Port ID + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. Unspecified tags remain unchanged. Read-only tags are always preserved and @@ -716,12 +867,19 @@ async def update( if not floating_ip_id: raise ValueError(f"Expected a non-empty value for `floating_ip_id` but received {floating_ip_id!r}") return await self._patch( - f"/cloud/v1/floatingips/{project_id}/{region_id}/{floating_ip_id}", - body=await async_maybe_transform({"tags": tags}, floating_ip_update_params.FloatingIPUpdateParams), + f"/cloud/v2/floatingips/{project_id}/{region_id}/{floating_ip_id}", + body=await async_maybe_transform( + { + "fixed_ip_address": fixed_ip_address, + "port_id": port_id, + "tags": tags, + }, + floating_ip_update_params.FloatingIPUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=FloatingIP, + cast_to=TaskIDList, ) def list( @@ -1046,6 +1204,56 @@ async def create_and_poll( timeout=timeout, ) + async def update_and_poll( + self, + floating_ip_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + fixed_ip_address: Optional[str] | Omit = omit, + port_id: Optional[str] | Omit = omit, + tags: Optional[TagUpdateMapParam] | 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, + ) -> FloatingIP: + """ + Update floating IP 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( + floating_ip_id=floating_ip_id, + project_id=project_id, + region_id=region_id, + fixed_ip_address=fixed_ip_address, + port_id=port_id, + tags=tags, + 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( + floating_ip_id=floating_ip_id, + project_id=project_id, + region_id=region_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + async def delete_and_poll( self, floating_ip_id: str, @@ -1090,10 +1298,8 @@ def __init__(self, floating_ips: FloatingIPsResource) -> None: self.create = to_raw_response_wrapper( floating_ips.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - floating_ips.update, # pyright: ignore[reportDeprecated], - ) + self.update = to_raw_response_wrapper( + floating_ips.update, ) self.list = to_raw_response_wrapper( floating_ips.list, @@ -1117,6 +1323,9 @@ def __init__(self, floating_ips: FloatingIPsResource) -> None: self.create_and_poll = to_raw_response_wrapper( floating_ips.create_and_poll, ) + self.update_and_poll = to_raw_response_wrapper( + floating_ips.update_and_poll, + ) self.delete_and_poll = to_raw_response_wrapper( floating_ips.delete_and_poll, ) @@ -1129,10 +1338,8 @@ def __init__(self, floating_ips: AsyncFloatingIPsResource) -> None: self.create = async_to_raw_response_wrapper( floating_ips.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - floating_ips.update, # pyright: ignore[reportDeprecated], - ) + self.update = async_to_raw_response_wrapper( + floating_ips.update, ) self.list = async_to_raw_response_wrapper( floating_ips.list, @@ -1156,6 +1363,9 @@ def __init__(self, floating_ips: AsyncFloatingIPsResource) -> None: self.create_and_poll = async_to_raw_response_wrapper( floating_ips.create_and_poll, ) + self.update_and_poll = async_to_raw_response_wrapper( + floating_ips.update_and_poll, + ) self.delete_and_poll = async_to_raw_response_wrapper( floating_ips.delete_and_poll, ) @@ -1168,10 +1378,8 @@ def __init__(self, floating_ips: FloatingIPsResource) -> None: self.create = to_streamed_response_wrapper( floating_ips.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - floating_ips.update, # pyright: ignore[reportDeprecated], - ) + self.update = to_streamed_response_wrapper( + floating_ips.update, ) self.list = to_streamed_response_wrapper( floating_ips.list, @@ -1195,6 +1403,9 @@ def __init__(self, floating_ips: FloatingIPsResource) -> None: self.create_and_poll = to_streamed_response_wrapper( floating_ips.create_and_poll, ) + self.update_and_poll = to_streamed_response_wrapper( + floating_ips.update_and_poll, + ) self.delete_and_poll = to_streamed_response_wrapper( floating_ips.delete_and_poll, ) @@ -1207,10 +1418,8 @@ def __init__(self, floating_ips: AsyncFloatingIPsResource) -> None: self.create = async_to_streamed_response_wrapper( floating_ips.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - floating_ips.update, # pyright: ignore[reportDeprecated], - ) + self.update = async_to_streamed_response_wrapper( + floating_ips.update, ) self.list = async_to_streamed_response_wrapper( floating_ips.list, @@ -1234,6 +1443,9 @@ def __init__(self, floating_ips: AsyncFloatingIPsResource) -> None: self.create_and_poll = async_to_streamed_response_wrapper( floating_ips.create_and_poll, ) + self.update_and_poll = async_to_streamed_response_wrapper( + floating_ips.update_and_poll, + ) self.delete_and_poll = async_to_streamed_response_wrapper( floating_ips.delete_and_poll, ) diff --git a/src/gcore/resources/cloud/security_groups/security_groups.py b/src/gcore/resources/cloud/security_groups/security_groups.py index a287b6bf..d864ba91 100644 --- a/src/gcore/resources/cloud/security_groups/security_groups.py +++ b/src/gcore/resources/cloud/security_groups/security_groups.py @@ -2,8 +2,7 @@ from __future__ import annotations -import typing_extensions -from typing import Iterable, Optional +from typing import Dict, Iterable, Optional import httpx @@ -15,7 +14,7 @@ RulesResourceWithStreamingResponse, AsyncRulesResourceWithStreamingResponse, ) -from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ...._types import NOT_GIVEN, Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource @@ -33,6 +32,7 @@ security_group_update_params, ) from ...._base_client import AsyncPaginator, make_request_options +from ....types.cloud.task_id_list import TaskIDList from ....types.cloud.security_group import SecurityGroup from ....types.cloud.tag_update_map_param import TagUpdateMapParam @@ -63,32 +63,45 @@ def with_streaming_response(self) -> SecurityGroupsResourceWithStreamingResponse """ return SecurityGroupsResourceWithStreamingResponse(self) - @typing_extensions.deprecated("deprecated") def create( self, *, project_id: int | None = None, region_id: int | None = None, - security_group: security_group_create_params.SecurityGroup, - instances: SequenceNotStr[str] | Omit = omit, + name: str, + description: str | Omit = omit, + rules: Iterable[security_group_create_params.Rule] | Omit = omit, + tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SecurityGroup: - """ - **Deprecated** Use `/v2/security_groups//` instead. + ) -> TaskIDList: + """Creates a new security group with the specified configuration. + + If no egress + rules are provided, default set of egress rules will be applied If rules are + explicitly set to empty, no rules will be created. Args: project_id: Project ID region_id: Region ID - security_group: Security group + name: Security group name - instances: List of instances + description: Security group description + + rules: Security group rules + + tags: Key-value tags to associate with the resource. A tag is a key-value pair that + can be associated with a resource, enabling efficient filtering and grouping for + better organization and management. Both tag keys and values have a maximum + length of 255 characters. Some tags are read-only and cannot be modified by the + user. Tags are also integrated with cost reports, allowing cost data to be + filtered based on tag keys or values. extra_headers: Send extra headers @@ -103,29 +116,31 @@ def create( if region_id is None: region_id = self._client._get_cloud_region_id_path_param() return self._post( - f"/cloud/v1/securitygroups/{project_id}/{region_id}", + f"/cloud/v2/security_groups/{project_id}/{region_id}", body=maybe_transform( { - "security_group": security_group, - "instances": instances, + "name": name, + "description": description, + "rules": rules, + "tags": tags, }, security_group_create_params.SecurityGroupCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=SecurityGroup, + cast_to=TaskIDList, ) - @typing_extensions.deprecated("deprecated") def update( self, group_id: str, *, project_id: int | None = None, region_id: int | None = None, - changed_rules: Iterable[security_group_update_params.ChangedRule] | Omit = omit, + description: str | Omit = omit, name: str | Omit = omit, + rules: Iterable[security_group_update_params.Rule] | Omit = omit, tags: Optional[TagUpdateMapParam] | 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. @@ -133,22 +148,41 @@ def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SecurityGroup: + ) -> TaskIDList: """ - **Deprecated** Use `/v2/security_groups///` - instead. + Updates the specified security group with the provided changes. + + **Behavior:** + + - Simple fields (name, description) will be updated if provided + - Undefined fields will remain unchanged + - If no change is detected for a specific field compared to the current security + group state, that field will be skipped + - If no changes are detected at all across all fields, no task will be created + and an empty task list will be returned + + **Important - Security Group Rules:** + + - Rules must be specified completely as the desired final state + - The system compares the provided rules against the current state + - Rules that exist in the request but not in the current state will be added + - Rules that exist in the current state but not in the request will be removed + - To keep existing rules, they must be included in the request alongside any new + rules Args: project_id: Project ID region_id: Region ID - group_id: Group ID + group_id: Security group ID - changed_rules: List of rules to create or delete + description: Security group description name: Name + rules: Security group rules + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. Unspecified tags remain unchanged. Read-only tags are always preserved and @@ -191,11 +225,12 @@ def update( if not group_id: raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") return self._patch( - f"/cloud/v1/securitygroups/{project_id}/{region_id}/{group_id}", + f"/cloud/v2/security_groups/{project_id}/{region_id}/{group_id}", body=maybe_transform( { - "changed_rules": changed_rules, + "description": description, "name": name, + "rules": rules, "tags": tags, }, security_group_update_params.SecurityGroupUpdateParams, @@ -203,7 +238,7 @@ def update( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=SecurityGroup, + cast_to=TaskIDList, ) def list( @@ -460,6 +495,112 @@ def revert_to_default( cast_to=SecurityGroup, ) + def create_and_poll( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + name: str, + description: str | Omit = omit, + rules: Iterable[security_group_create_params.Rule] | Omit = omit, + tags: Dict[str, str] | 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, + ) -> SecurityGroup: + """ + Create security group 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.create( + project_id=project_id, + region_id=region_id, + name=name, + description=description, + rules=rules, + tags=tags, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + if not response.tasks: + raise ValueError("Expected at least one task to be created") + task = 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, + ) + if task.created_resources is None or task.created_resources.security_groups is None: + raise ValueError("Task completed but created_resources or security_groups is missing") + security_group_id = task.created_resources.security_groups[0] + return self.get( + group_id=security_group_id, + project_id=project_id, + region_id=region_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + def update_and_poll( + self, + group_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + description: str | Omit = omit, + name: str | Omit = omit, + rules: Iterable[security_group_update_params.Rule] | Omit = omit, + tags: Optional[TagUpdateMapParam] | 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, + ) -> SecurityGroup: + """ + Update security group 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( + group_id=group_id, + project_id=project_id, + region_id=region_id, + description=description, + name=name, + rules=rules, + tags=tags, + 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( + group_id=group_id, + project_id=project_id, + region_id=region_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + class AsyncSecurityGroupsResource(AsyncAPIResource): @cached_property @@ -485,32 +626,45 @@ def with_streaming_response(self) -> AsyncSecurityGroupsResourceWithStreamingRes """ return AsyncSecurityGroupsResourceWithStreamingResponse(self) - @typing_extensions.deprecated("deprecated") async def create( self, *, project_id: int | None = None, region_id: int | None = None, - security_group: security_group_create_params.SecurityGroup, - instances: SequenceNotStr[str] | Omit = omit, + name: str, + description: str | Omit = omit, + rules: Iterable[security_group_create_params.Rule] | Omit = omit, + tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SecurityGroup: - """ - **Deprecated** Use `/v2/security_groups//` instead. + ) -> TaskIDList: + """Creates a new security group with the specified configuration. + + If no egress + rules are provided, default set of egress rules will be applied If rules are + explicitly set to empty, no rules will be created. Args: project_id: Project ID region_id: Region ID - security_group: Security group + name: Security group name - instances: List of instances + description: Security group description + + rules: Security group rules + + tags: Key-value tags to associate with the resource. A tag is a key-value pair that + can be associated with a resource, enabling efficient filtering and grouping for + better organization and management. Both tag keys and values have a maximum + length of 255 characters. Some tags are read-only and cannot be modified by the + user. Tags are also integrated with cost reports, allowing cost data to be + filtered based on tag keys or values. extra_headers: Send extra headers @@ -525,29 +679,31 @@ async def create( if region_id is None: region_id = self._client._get_cloud_region_id_path_param() return await self._post( - f"/cloud/v1/securitygroups/{project_id}/{region_id}", + f"/cloud/v2/security_groups/{project_id}/{region_id}", body=await async_maybe_transform( { - "security_group": security_group, - "instances": instances, + "name": name, + "description": description, + "rules": rules, + "tags": tags, }, security_group_create_params.SecurityGroupCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=SecurityGroup, + cast_to=TaskIDList, ) - @typing_extensions.deprecated("deprecated") async def update( self, group_id: str, *, project_id: int | None = None, region_id: int | None = None, - changed_rules: Iterable[security_group_update_params.ChangedRule] | Omit = omit, + description: str | Omit = omit, name: str | Omit = omit, + rules: Iterable[security_group_update_params.Rule] | Omit = omit, tags: Optional[TagUpdateMapParam] | 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. @@ -555,22 +711,41 @@ async def update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SecurityGroup: + ) -> TaskIDList: """ - **Deprecated** Use `/v2/security_groups///` - instead. + Updates the specified security group with the provided changes. + + **Behavior:** + + - Simple fields (name, description) will be updated if provided + - Undefined fields will remain unchanged + - If no change is detected for a specific field compared to the current security + group state, that field will be skipped + - If no changes are detected at all across all fields, no task will be created + and an empty task list will be returned + + **Important - Security Group Rules:** + + - Rules must be specified completely as the desired final state + - The system compares the provided rules against the current state + - Rules that exist in the request but not in the current state will be added + - Rules that exist in the current state but not in the request will be removed + - To keep existing rules, they must be included in the request alongside any new + rules Args: project_id: Project ID region_id: Region ID - group_id: Group ID + group_id: Security group ID - changed_rules: List of rules to create or delete + description: Security group description name: Name + rules: Security group rules + tags: Update key-value tags using JSON Merge Patch semantics (RFC 7386). Provide key-value pairs to add or update tags. Set tag values to `null` to remove tags. Unspecified tags remain unchanged. Read-only tags are always preserved and @@ -613,11 +788,12 @@ async def update( if not group_id: raise ValueError(f"Expected a non-empty value for `group_id` but received {group_id!r}") return await self._patch( - f"/cloud/v1/securitygroups/{project_id}/{region_id}/{group_id}", + f"/cloud/v2/security_groups/{project_id}/{region_id}/{group_id}", body=await async_maybe_transform( { - "changed_rules": changed_rules, + "description": description, "name": name, + "rules": rules, "tags": tags, }, security_group_update_params.SecurityGroupUpdateParams, @@ -625,7 +801,7 @@ async def update( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=SecurityGroup, + cast_to=TaskIDList, ) def list( @@ -882,20 +1058,122 @@ async def revert_to_default( cast_to=SecurityGroup, ) + async def create_and_poll( + self, + *, + project_id: int | None = None, + region_id: int | None = None, + name: str, + description: str | Omit = omit, + rules: Iterable[security_group_create_params.Rule] | Omit = omit, + tags: Dict[str, str] | 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, + ) -> SecurityGroup: + """ + Create security group 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.create( + project_id=project_id, + region_id=region_id, + name=name, + description=description, + rules=rules, + tags=tags, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + if not response.tasks: + raise ValueError("Expected at least one task to be created") + task = 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, + ) + if task.created_resources is None or task.created_resources.security_groups is None: + raise ValueError("Task completed but created_resources or security_groups is missing") + security_group_id = task.created_resources.security_groups[0] + return await self.get( + group_id=security_group_id, + project_id=project_id, + region_id=region_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + + async def update_and_poll( + self, + group_id: str, + *, + project_id: int | None = None, + region_id: int | None = None, + description: str | Omit = omit, + name: str | Omit = omit, + rules: Iterable[security_group_update_params.Rule] | Omit = omit, + tags: Optional[TagUpdateMapParam] | 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, + ) -> SecurityGroup: + """ + Update security group 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( + group_id=group_id, + project_id=project_id, + region_id=region_id, + description=description, + name=name, + rules=rules, + tags=tags, + 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( + group_id=group_id, + project_id=project_id, + region_id=region_id, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + ) + class SecurityGroupsResourceWithRawResponse: def __init__(self, security_groups: SecurityGroupsResource) -> None: self._security_groups = security_groups - self.create = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - security_groups.create, # pyright: ignore[reportDeprecated], - ) + self.create = to_raw_response_wrapper( + security_groups.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - to_raw_response_wrapper( - security_groups.update, # pyright: ignore[reportDeprecated], - ) + self.update = to_raw_response_wrapper( + security_groups.update, ) self.list = to_raw_response_wrapper( security_groups.list, @@ -912,6 +1190,12 @@ def __init__(self, security_groups: SecurityGroupsResource) -> None: self.revert_to_default = to_raw_response_wrapper( security_groups.revert_to_default, ) + self.create_and_poll = to_raw_response_wrapper( + security_groups.create_and_poll, + ) + self.update_and_poll = to_raw_response_wrapper( + security_groups.update_and_poll, + ) @cached_property def rules(self) -> RulesResourceWithRawResponse: @@ -922,15 +1206,11 @@ class AsyncSecurityGroupsResourceWithRawResponse: def __init__(self, security_groups: AsyncSecurityGroupsResource) -> None: self._security_groups = security_groups - self.create = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - security_groups.create, # pyright: ignore[reportDeprecated], - ) + self.create = async_to_raw_response_wrapper( + security_groups.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - async_to_raw_response_wrapper( - security_groups.update, # pyright: ignore[reportDeprecated], - ) + self.update = async_to_raw_response_wrapper( + security_groups.update, ) self.list = async_to_raw_response_wrapper( security_groups.list, @@ -947,6 +1227,12 @@ def __init__(self, security_groups: AsyncSecurityGroupsResource) -> None: self.revert_to_default = async_to_raw_response_wrapper( security_groups.revert_to_default, ) + self.create_and_poll = async_to_raw_response_wrapper( + security_groups.create_and_poll, + ) + self.update_and_poll = async_to_raw_response_wrapper( + security_groups.update_and_poll, + ) @cached_property def rules(self) -> AsyncRulesResourceWithRawResponse: @@ -957,15 +1243,11 @@ class SecurityGroupsResourceWithStreamingResponse: def __init__(self, security_groups: SecurityGroupsResource) -> None: self._security_groups = security_groups - self.create = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - security_groups.create, # pyright: ignore[reportDeprecated], - ) + self.create = to_streamed_response_wrapper( + security_groups.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - to_streamed_response_wrapper( - security_groups.update, # pyright: ignore[reportDeprecated], - ) + self.update = to_streamed_response_wrapper( + security_groups.update, ) self.list = to_streamed_response_wrapper( security_groups.list, @@ -982,6 +1264,12 @@ def __init__(self, security_groups: SecurityGroupsResource) -> None: self.revert_to_default = to_streamed_response_wrapper( security_groups.revert_to_default, ) + self.create_and_poll = to_streamed_response_wrapper( + security_groups.create_and_poll, + ) + self.update_and_poll = to_streamed_response_wrapper( + security_groups.update_and_poll, + ) @cached_property def rules(self) -> RulesResourceWithStreamingResponse: @@ -992,15 +1280,11 @@ class AsyncSecurityGroupsResourceWithStreamingResponse: def __init__(self, security_groups: AsyncSecurityGroupsResource) -> None: self._security_groups = security_groups - self.create = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - security_groups.create, # pyright: ignore[reportDeprecated], - ) + self.create = async_to_streamed_response_wrapper( + security_groups.create, ) - self.update = ( # pyright: ignore[reportDeprecated] - async_to_streamed_response_wrapper( - security_groups.update, # pyright: ignore[reportDeprecated], - ) + self.update = async_to_streamed_response_wrapper( + security_groups.update, ) self.list = async_to_streamed_response_wrapper( security_groups.list, @@ -1017,6 +1301,12 @@ def __init__(self, security_groups: AsyncSecurityGroupsResource) -> None: self.revert_to_default = async_to_streamed_response_wrapper( security_groups.revert_to_default, ) + self.create_and_poll = async_to_streamed_response_wrapper( + security_groups.create_and_poll, + ) + self.update_and_poll = async_to_streamed_response_wrapper( + security_groups.update_and_poll, + ) @cached_property def rules(self) -> AsyncRulesResourceWithStreamingResponse: diff --git a/src/gcore/types/cloud/floating_ip_update_params.py b/src/gcore/types/cloud/floating_ip_update_params.py index 13dfa1d0..65e87c02 100644 --- a/src/gcore/types/cloud/floating_ip_update_params.py +++ b/src/gcore/types/cloud/floating_ip_update_params.py @@ -17,6 +17,12 @@ class FloatingIPUpdateParams(TypedDict, total=False): region_id: int """Region ID""" + fixed_ip_address: Optional[str] + """Fixed IP address""" + + port_id: Optional[str] + """Port ID""" + tags: Optional[TagUpdateMapParam] """Update key-value tags using JSON Merge Patch semantics (RFC 7386). diff --git a/src/gcore/types/cloud/security_group_create_params.py b/src/gcore/types/cloud/security_group_create_params.py index b932ad13..e660dee3 100644 --- a/src/gcore/types/cloud/security_group_create_params.py +++ b/src/gcore/types/cloud/security_group_create_params.py @@ -5,9 +5,7 @@ from typing import Dict, Iterable, Optional from typing_extensions import Literal, Required, TypedDict -from ..._types import SequenceNotStr - -__all__ = ["SecurityGroupCreateParams", "SecurityGroup", "SecurityGroupSecurityGroupRule"] +__all__ = ["SecurityGroupCreateParams", "Rule"] class SecurityGroupCreateParams(TypedDict, total=False): @@ -17,14 +15,27 @@ class SecurityGroupCreateParams(TypedDict, total=False): region_id: int """Region ID""" - security_group: Required[SecurityGroup] - """Security group""" + name: Required[str] + """Security group name""" + + description: str + """Security group description""" + + rules: Iterable[Rule] + """Security group rules""" + + tags: Dict[str, str] + """Key-value tags to associate with the resource. - instances: SequenceNotStr[str] - """List of instances""" + A tag is a key-value pair that can be associated with a resource, enabling + efficient filtering and grouping for better organization and management. Both + tag keys and values have a maximum length of 255 characters. Some tags are + read-only and cannot be modified by the user. Tags are also integrated with cost + reports, allowing cost data to be filtered based on tag keys or values. + """ -class SecurityGroupSecurityGroupRule(TypedDict, total=False): +class Rule(TypedDict, total=False): direction: Required[Literal["egress", "ingress"]] """ Ingress or egress, which is the direction in which the security group is applied @@ -75,26 +86,3 @@ class SecurityGroupSecurityGroupRule(TypedDict, total=False): remote_ip_prefix: Optional[str] """The remote IP prefix that is matched by this security group rule""" - - -class SecurityGroup(TypedDict, total=False): - """Security group""" - - name: Required[str] - """Security group name""" - - description: Optional[str] - """Security group description""" - - security_group_rules: Iterable[SecurityGroupSecurityGroupRule] - """Security group rules""" - - tags: Dict[str, str] - """Key-value tags to associate with the resource. - - A tag is a key-value pair that can be associated with a resource, enabling - efficient filtering and grouping for better organization and management. Both - tag keys and values have a maximum length of 255 characters. Some tags are - read-only and cannot be modified by the user. Tags are also integrated with cost - reports, allowing cost data to be filtered based on tag keys or values. - """ diff --git a/src/gcore/types/cloud/security_group_update_params.py b/src/gcore/types/cloud/security_group_update_params.py index 1ac56005..657e9807 100644 --- a/src/gcore/types/cloud/security_group_update_params.py +++ b/src/gcore/types/cloud/security_group_update_params.py @@ -3,11 +3,11 @@ from __future__ import annotations from typing import Iterable, Optional -from typing_extensions import Literal, Required, TypedDict +from typing_extensions import Literal, TypedDict from .tag_update_map_param import TagUpdateMapParam -__all__ = ["SecurityGroupUpdateParams", "ChangedRule"] +__all__ = ["SecurityGroupUpdateParams", "Rule"] class SecurityGroupUpdateParams(TypedDict, total=False): @@ -17,12 +17,15 @@ class SecurityGroupUpdateParams(TypedDict, total=False): region_id: int """Region ID""" - changed_rules: Iterable[ChangedRule] - """List of rules to create or delete""" + description: str + """Security group description""" name: str """Name""" + rules: Iterable[Rule] + """Security group rules""" + tags: Optional[TagUpdateMapParam] """Update key-value tags using JSON Merge Patch semantics (RFC 7386). @@ -54,10 +57,7 @@ class SecurityGroupUpdateParams(TypedDict, total=False): """ -class ChangedRule(TypedDict, total=False): - action: Required[Literal["create", "delete"]] - """Action for a rule""" - +class Rule(TypedDict, total=False): description: str """Security grpup rule description""" @@ -67,7 +67,7 @@ class ChangedRule(TypedDict, total=False): applied """ - ethertype: Optional[Literal["IPv4", "IPv6"]] + ethertype: Literal["IPv4", "IPv6"] """ Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules. @@ -107,11 +107,8 @@ class ChangedRule(TypedDict, total=False): ] """Protocol""" - remote_group_id: Optional[str] + remote_group_id: str """The remote group UUID to associate with this security group rule""" - remote_ip_prefix: Optional[str] + remote_ip_prefix: str """The remote IP prefix that is matched by this security group rule""" - - security_group_rule_id: Optional[str] - """UUID of rule to be deleted. Required for action 'delete' only""" diff --git a/tests/api_resources/cloud/test_floating_ips.py b/tests/api_resources/cloud/test_floating_ips.py index b2394861..97ff5795 100644 --- a/tests/api_resources/cloud/test_floating_ips.py +++ b/tests/api_resources/cloud/test_floating_ips.py @@ -71,66 +71,61 @@ def test_streaming_response_create(self, client: Gcore) -> None: @parametrize def test_method_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - floating_ip = client.cloud.floating_ips.update( - floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", - project_id=1, - region_id=1, - ) - - assert_matches_type(FloatingIP, floating_ip, path=["response"]) + floating_ip = client.cloud.floating_ips.update( + floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", + project_id=1, + region_id=1, + ) + assert_matches_type(TaskIDList, floating_ip, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - floating_ip = client.cloud.floating_ips.update( - floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", - project_id=1, - region_id=1, - tags={"foo": "my-tag-value"}, - ) - - assert_matches_type(FloatingIP, floating_ip, path=["response"]) + floating_ip = client.cloud.floating_ips.update( + floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", + project_id=1, + region_id=1, + fixed_ip_address="192.168.10.15", + port_id="ee2402d0-f0cd-4503-9b75-69be1d11c5f1", + tags={"foo": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, floating_ip, path=["response"]) @parametrize def test_raw_response_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - response = client.cloud.floating_ips.with_raw_response.update( - floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", - project_id=1, - region_id=1, - ) + response = client.cloud.floating_ips.with_raw_response.update( + floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", + project_id=1, + region_id=1, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" floating_ip = response.parse() - assert_matches_type(FloatingIP, floating_ip, path=["response"]) + assert_matches_type(TaskIDList, floating_ip, path=["response"]) @parametrize def test_streaming_response_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - with client.cloud.floating_ips.with_streaming_response.update( - floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", - project_id=1, - region_id=1, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with client.cloud.floating_ips.with_streaming_response.update( + floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", + project_id=1, + region_id=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - floating_ip = response.parse() - assert_matches_type(FloatingIP, floating_ip, path=["response"]) + floating_ip = response.parse() + assert_matches_type(TaskIDList, floating_ip, 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 `floating_ip_id` but received ''"): - client.cloud.floating_ips.with_raw_response.update( - floating_ip_id="", - project_id=1, - region_id=1, - ) + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip_id` but received ''"): + client.cloud.floating_ips.with_raw_response.update( + floating_ip_id="", + project_id=1, + region_id=1, + ) @parametrize def test_method_list(self, client: Gcore) -> None: @@ -443,66 +438,61 @@ 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): - floating_ip = await async_client.cloud.floating_ips.update( - floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", - project_id=1, - region_id=1, - ) - - assert_matches_type(FloatingIP, floating_ip, path=["response"]) + floating_ip = await async_client.cloud.floating_ips.update( + floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", + project_id=1, + region_id=1, + ) + assert_matches_type(TaskIDList, floating_ip, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - floating_ip = await async_client.cloud.floating_ips.update( - floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", - project_id=1, - region_id=1, - tags={"foo": "my-tag-value"}, - ) - - assert_matches_type(FloatingIP, floating_ip, path=["response"]) + floating_ip = await async_client.cloud.floating_ips.update( + floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", + project_id=1, + region_id=1, + fixed_ip_address="192.168.10.15", + port_id="ee2402d0-f0cd-4503-9b75-69be1d11c5f1", + tags={"foo": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, floating_ip, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.cloud.floating_ips.with_raw_response.update( - floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", - project_id=1, - region_id=1, - ) + response = await async_client.cloud.floating_ips.with_raw_response.update( + floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", + project_id=1, + region_id=1, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" floating_ip = await response.parse() - assert_matches_type(FloatingIP, floating_ip, path=["response"]) + assert_matches_type(TaskIDList, floating_ip, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.cloud.floating_ips.with_streaming_response.update( - floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", - project_id=1, - region_id=1, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + async with async_client.cloud.floating_ips.with_streaming_response.update( + floating_ip_id="c64e5db1-5f1f-43ec-a8d9-5090df85b82d", + project_id=1, + region_id=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - floating_ip = await response.parse() - assert_matches_type(FloatingIP, floating_ip, path=["response"]) + floating_ip = await response.parse() + assert_matches_type(TaskIDList, floating_ip, 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 `floating_ip_id` but received ''"): - await async_client.cloud.floating_ips.with_raw_response.update( - floating_ip_id="", - project_id=1, - region_id=1, - ) + with pytest.raises(ValueError, match=r"Expected a non-empty value for `floating_ip_id` but received ''"): + await async_client.cloud.floating_ips.with_raw_response.update( + floating_ip_id="", + project_id=1, + region_id=1, + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: diff --git a/tests/api_resources/cloud/test_security_groups.py b/tests/api_resources/cloud/test_security_groups.py index 0b842fd6..2bd9402b 100644 --- a/tests/api_resources/cloud/test_security_groups.py +++ b/tests/api_resources/cloud/test_security_groups.py @@ -11,11 +11,10 @@ from tests.utils import assert_matches_type from gcore.pagination import SyncOffsetPage, AsyncOffsetPage from gcore.types.cloud import ( + TaskIDList, SecurityGroup, ) -# pyright: reportDeprecated=false - base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -24,150 +23,133 @@ class TestSecurityGroups: @parametrize def test_method_create(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - security_group = client.cloud.security_groups.create( - project_id=1, - region_id=1, - security_group={"name": "my_security_group"}, - ) - - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = client.cloud.security_groups.create( + project_id=1, + region_id=1, + name="my_security_group", + ) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - security_group = client.cloud.security_groups.create( - project_id=1, - region_id=1, - security_group={ - "name": "my_security_group", + security_group = client.cloud.security_groups.create( + project_id=1, + region_id=1, + name="my_security_group", + description="My security group description", + rules=[ + { + "direction": "ingress", "description": "Some description", - "security_group_rules": [ - { - "direction": "ingress", - "description": "Some description", - "ethertype": "IPv4", - "port_range_max": 80, - "port_range_min": 80, - "protocol": "tcp", - "remote_group_id": "00000000-0000-4000-8000-000000000000", - "remote_ip_prefix": "10.0.0.0/8", - } - ], - "tags": {"my-tag": "my-tag-value"}, - }, - instances=["00000000-0000-4000-8000-000000000000"], - ) - - assert_matches_type(SecurityGroup, security_group, path=["response"]) + "ethertype": "IPv4", + "port_range_max": 80, + "port_range_min": 80, + "protocol": "tcp", + "remote_group_id": "00000000-0000-4000-8000-000000000000", + "remote_ip_prefix": "10.0.0.0/8", + } + ], + tags={"my-tag": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize def test_raw_response_create(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - response = client.cloud.security_groups.with_raw_response.create( - project_id=1, - region_id=1, - security_group={"name": "my_security_group"}, - ) + response = client.cloud.security_groups.with_raw_response.create( + project_id=1, + region_id=1, + name="my_security_group", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" security_group = response.parse() - assert_matches_type(SecurityGroup, security_group, path=["response"]) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize def test_streaming_response_create(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - with client.cloud.security_groups.with_streaming_response.create( - project_id=1, - region_id=1, - security_group={"name": "my_security_group"}, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with client.cloud.security_groups.with_streaming_response.create( + project_id=1, + region_id=1, + name="my_security_group", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - security_group = response.parse() - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = response.parse() + assert_matches_type(TaskIDList, security_group, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - security_group = client.cloud.security_groups.update( - group_id="024a29e9-b4b7-4c91-9a46-505be123d9f8", - project_id=1, - region_id=1, - ) - - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = client.cloud.security_groups.update( + group_id="00000000-0000-4000-8000-000000000000", + project_id=1, + region_id=1, + ) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - security_group = client.cloud.security_groups.update( - group_id="024a29e9-b4b7-4c91-9a46-505be123d9f8", - project_id=1, - region_id=1, - changed_rules=[ - { - "action": "delete", - "description": "Some description", - "direction": "egress", - "ethertype": "IPv4", - "port_range_max": 80, - "port_range_min": 80, - "protocol": "tcp", - "remote_group_id": "00000000-0000-4000-8000-000000000000", - "remote_ip_prefix": "10.0.0.0/8", - "security_group_rule_id": "00000000-0000-4000-8000-000000000000", - } - ], - name="some_name", - tags={"foo": "my-tag-value"}, - ) - - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = client.cloud.security_groups.update( + group_id="00000000-0000-4000-8000-000000000000", + project_id=1, + region_id=1, + description="Some description", + name="some_name", + rules=[ + { + "description": "Some description", + "direction": "egress", + "ethertype": "IPv4", + "port_range_max": 80, + "port_range_min": 80, + "protocol": "tcp", + "remote_group_id": "00000000-0000-4000-8000-000000000000", + "remote_ip_prefix": "10.0.0.0/8", + } + ], + tags={"foo": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize def test_raw_response_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - response = client.cloud.security_groups.with_raw_response.update( - group_id="024a29e9-b4b7-4c91-9a46-505be123d9f8", - project_id=1, - region_id=1, - ) + response = client.cloud.security_groups.with_raw_response.update( + group_id="00000000-0000-4000-8000-000000000000", + project_id=1, + region_id=1, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" security_group = response.parse() - assert_matches_type(SecurityGroup, security_group, path=["response"]) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize def test_streaming_response_update(self, client: Gcore) -> None: - with pytest.warns(DeprecationWarning): - with client.cloud.security_groups.with_streaming_response.update( - group_id="024a29e9-b4b7-4c91-9a46-505be123d9f8", - project_id=1, - region_id=1, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + with client.cloud.security_groups.with_streaming_response.update( + group_id="00000000-0000-4000-8000-000000000000", + project_id=1, + region_id=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - security_group = response.parse() - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = response.parse() + assert_matches_type(TaskIDList, security_group, 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 `group_id` but received ''"): - client.cloud.security_groups.with_raw_response.update( - group_id="", - project_id=1, - region_id=1, - ) + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + client.cloud.security_groups.with_raw_response.update( + group_id="", + project_id=1, + region_id=1, + ) @parametrize def test_method_list(self, client: Gcore) -> None: @@ -412,150 +394,133 @@ class TestAsyncSecurityGroups: @parametrize async def test_method_create(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - security_group = await async_client.cloud.security_groups.create( - project_id=1, - region_id=1, - security_group={"name": "my_security_group"}, - ) - - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = await async_client.cloud.security_groups.create( + project_id=1, + region_id=1, + name="my_security_group", + ) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - security_group = await async_client.cloud.security_groups.create( - project_id=1, - region_id=1, - security_group={ - "name": "my_security_group", + security_group = await async_client.cloud.security_groups.create( + project_id=1, + region_id=1, + name="my_security_group", + description="My security group description", + rules=[ + { + "direction": "ingress", "description": "Some description", - "security_group_rules": [ - { - "direction": "ingress", - "description": "Some description", - "ethertype": "IPv4", - "port_range_max": 80, - "port_range_min": 80, - "protocol": "tcp", - "remote_group_id": "00000000-0000-4000-8000-000000000000", - "remote_ip_prefix": "10.0.0.0/8", - } - ], - "tags": {"my-tag": "my-tag-value"}, - }, - instances=["00000000-0000-4000-8000-000000000000"], - ) - - assert_matches_type(SecurityGroup, security_group, path=["response"]) + "ethertype": "IPv4", + "port_range_max": 80, + "port_range_min": 80, + "protocol": "tcp", + "remote_group_id": "00000000-0000-4000-8000-000000000000", + "remote_ip_prefix": "10.0.0.0/8", + } + ], + tags={"my-tag": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.cloud.security_groups.with_raw_response.create( - project_id=1, - region_id=1, - security_group={"name": "my_security_group"}, - ) + response = await async_client.cloud.security_groups.with_raw_response.create( + project_id=1, + region_id=1, + name="my_security_group", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" security_group = await response.parse() - assert_matches_type(SecurityGroup, security_group, path=["response"]) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.cloud.security_groups.with_streaming_response.create( - project_id=1, - region_id=1, - security_group={"name": "my_security_group"}, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + async with async_client.cloud.security_groups.with_streaming_response.create( + project_id=1, + region_id=1, + name="my_security_group", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - security_group = await response.parse() - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = await response.parse() + assert_matches_type(TaskIDList, security_group, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_update(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - security_group = await async_client.cloud.security_groups.update( - group_id="024a29e9-b4b7-4c91-9a46-505be123d9f8", - project_id=1, - region_id=1, - ) - - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = await async_client.cloud.security_groups.update( + group_id="00000000-0000-4000-8000-000000000000", + project_id=1, + region_id=1, + ) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - security_group = await async_client.cloud.security_groups.update( - group_id="024a29e9-b4b7-4c91-9a46-505be123d9f8", - project_id=1, - region_id=1, - changed_rules=[ - { - "action": "delete", - "description": "Some description", - "direction": "egress", - "ethertype": "IPv4", - "port_range_max": 80, - "port_range_min": 80, - "protocol": "tcp", - "remote_group_id": "00000000-0000-4000-8000-000000000000", - "remote_ip_prefix": "10.0.0.0/8", - "security_group_rule_id": "00000000-0000-4000-8000-000000000000", - } - ], - name="some_name", - tags={"foo": "my-tag-value"}, - ) - - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = await async_client.cloud.security_groups.update( + group_id="00000000-0000-4000-8000-000000000000", + project_id=1, + region_id=1, + description="Some description", + name="some_name", + rules=[ + { + "description": "Some description", + "direction": "egress", + "ethertype": "IPv4", + "port_range_max": 80, + "port_range_min": 80, + "protocol": "tcp", + "remote_group_id": "00000000-0000-4000-8000-000000000000", + "remote_ip_prefix": "10.0.0.0/8", + } + ], + tags={"foo": "my-tag-value"}, + ) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - response = await async_client.cloud.security_groups.with_raw_response.update( - group_id="024a29e9-b4b7-4c91-9a46-505be123d9f8", - project_id=1, - region_id=1, - ) + response = await async_client.cloud.security_groups.with_raw_response.update( + group_id="00000000-0000-4000-8000-000000000000", + project_id=1, + region_id=1, + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" security_group = await response.parse() - assert_matches_type(SecurityGroup, security_group, path=["response"]) + assert_matches_type(TaskIDList, security_group, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncGcore) -> None: - with pytest.warns(DeprecationWarning): - async with async_client.cloud.security_groups.with_streaming_response.update( - group_id="024a29e9-b4b7-4c91-9a46-505be123d9f8", - project_id=1, - region_id=1, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" + async with async_client.cloud.security_groups.with_streaming_response.update( + group_id="00000000-0000-4000-8000-000000000000", + project_id=1, + region_id=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" - security_group = await response.parse() - assert_matches_type(SecurityGroup, security_group, path=["response"]) + security_group = await response.parse() + assert_matches_type(TaskIDList, security_group, 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 `group_id` but received ''"): - await async_client.cloud.security_groups.with_raw_response.update( - group_id="", - project_id=1, - region_id=1, - ) + with pytest.raises(ValueError, match=r"Expected a non-empty value for `group_id` but received ''"): + await async_client.cloud.security_groups.with_raw_response.update( + group_id="", + project_id=1, + region_id=1, + ) @parametrize async def test_method_list(self, async_client: AsyncGcore) -> None: