From 973f338a151c90b37ac2cc495254b1acf0ac7c40 Mon Sep 17 00:00:00 2001 From: scaleway-bot Date: Tue, 29 Sep 2026 14:11:55 +0000 Subject: [PATCH] feat: update generated APIs --- .../scaleway_async/iam/v1alpha1/__init__.py | 2 + .../scaleway_async/iam/v1alpha1/api.py | 28 ++ .../iam/v1alpha1/marshalling.py | 72 ++-- .../scaleway_async/iam/v1alpha1/types.py | 5 + .../instance/v2alpha1/__init__.py | 6 + .../scaleway_async/instance/v2alpha1/api.py | 34 ++ .../instance/v2alpha1/marshalling.py | 48 +++ .../scaleway_async/instance/v2alpha1/types.py | 37 ++ .../scaleway_async/k8s/v1/marshalling.py | 14 +- scaleway-async/scaleway_async/k8s/v1/types.py | 10 +- .../mailbox/v1alpha1/__init__.py | 26 ++ .../scaleway_async/mailbox/v1alpha1/api.py | 347 ++++++++++++++++++ .../mailbox/v1alpha1/content.py | 9 + .../mailbox/v1alpha1/marshalling.py | 162 ++++++++ .../scaleway_async/mailbox/v1alpha1/types.py | 199 ++++++++++ scaleway/scaleway/iam/v1alpha1/__init__.py | 2 + scaleway/scaleway/iam/v1alpha1/api.py | 28 ++ scaleway/scaleway/iam/v1alpha1/marshalling.py | 72 ++-- scaleway/scaleway/iam/v1alpha1/types.py | 5 + .../scaleway/instance/v2alpha1/__init__.py | 6 + scaleway/scaleway/instance/v2alpha1/api.py | 34 ++ .../scaleway/instance/v2alpha1/marshalling.py | 48 +++ scaleway/scaleway/instance/v2alpha1/types.py | 37 ++ scaleway/scaleway/k8s/v1/marshalling.py | 14 +- scaleway/scaleway/k8s/v1/types.py | 10 +- .../scaleway/mailbox/v1alpha1/__init__.py | 26 ++ scaleway/scaleway/mailbox/v1alpha1/api.py | 345 +++++++++++++++++ scaleway/scaleway/mailbox/v1alpha1/content.py | 9 + .../scaleway/mailbox/v1alpha1/marshalling.py | 162 ++++++++ scaleway/scaleway/mailbox/v1alpha1/types.py | 199 ++++++++++ 30 files changed, 1898 insertions(+), 98 deletions(-) diff --git a/scaleway-async/scaleway_async/iam/v1alpha1/__init__.py b/scaleway-async/scaleway_async/iam/v1alpha1/__init__.py index 052ad9df3..d4a0d8366 100644 --- a/scaleway-async/scaleway_async/iam/v1alpha1/__init__.py +++ b/scaleway-async/scaleway_async/iam/v1alpha1/__init__.py @@ -90,6 +90,7 @@ from .types import GetQuotumRequest from .types import GetSSHKeyRequest from .types import GetSamlCertificateRequest +from .types import GetScimTokenRequest from .types import GetUserConnectionsRequest from .types import GetUserConnectionsResponse from .types import GetUserRequest @@ -250,6 +251,7 @@ "GetQuotumRequest", "GetSSHKeyRequest", "GetSamlCertificateRequest", + "GetScimTokenRequest", "GetUserConnectionsRequest", "GetUserConnectionsResponse", "GetUserRequest", diff --git a/scaleway-async/scaleway_async/iam/v1alpha1/api.py b/scaleway-async/scaleway_async/iam/v1alpha1/api.py index e4ec08bb4..f4b284e40 100644 --- a/scaleway-async/scaleway_async/iam/v1alpha1/api.py +++ b/scaleway-async/scaleway_async/iam/v1alpha1/api.py @@ -112,6 +112,7 @@ ) from .marshalling import ( unmarshal_JWT, + unmarshal_ScimToken, unmarshal_APIKey, unmarshal_Application, unmarshal_Group, @@ -3590,6 +3591,33 @@ async def delete_scim_token( self._throw_on_error(res) + async def get_scim_token( + self, + *, + scim_token_id: str, + ) -> ScimToken: + """ + :param scim_token_id: + :return: :class:`ScimToken ` + + Usage: + :: + + result = await api.get_scim_token( + scim_token_id="example", + ) + """ + + param_scim_token_id = validate_path_param("scim_token_id", scim_token_id) + + res = self._request( + "GET", + f"/iam/v1alpha1/scim-tokens/{param_scim_token_id}", + ) + + self._throw_on_error(res) + return unmarshal_ScimToken(res.json()) + async def start_user_web_authn_registration( self, *, diff --git a/scaleway-async/scaleway_async/iam/v1alpha1/marshalling.py b/scaleway-async/scaleway_async/iam/v1alpha1/marshalling.py index 530ac4f4d..e30b457cd 100644 --- a/scaleway-async/scaleway_async/iam/v1alpha1/marshalling.py +++ b/scaleway-async/scaleway_async/iam/v1alpha1/marshalling.py @@ -23,6 +23,7 @@ SamlStatus, UserType, JWT, + ScimToken, APIKey, Application, Group, @@ -34,7 +35,6 @@ SamlCertificate, WebAuthnAuthenticator, User, - ScimToken, CreateScimTokenResponse, EncodedJWT, FinishUserWebAuthnRegistrationResponse, @@ -172,6 +172,41 @@ def unmarshal_JWT(data: Any) -> JWT: return JWT(**args) +def unmarshal_ScimToken(data: Any) -> ScimToken: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'ScimToken' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("id", None) + if field is not None: + args["id"] = field + else: + args["id"] = None + + field = data.get("scim_id", None) + if field is not None: + args["scim_id"] = field + else: + args["scim_id"] = None + + field = data.get("created_at", None) + if field is not None: + args["created_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["created_at"] = None + + field = data.get("expires_at", None) + if field is not None: + args["expires_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["expires_at"] = None + + return ScimToken(**args) + + def unmarshal_APIKey(data: Any) -> APIKey: if not isinstance(data, dict): raise TypeError( @@ -1037,41 +1072,6 @@ def unmarshal_User(data: Any) -> User: return User(**args) -def unmarshal_ScimToken(data: Any) -> ScimToken: - if not isinstance(data, dict): - raise TypeError( - "Unmarshalling the type 'ScimToken' failed as data isn't a dictionary." - ) - - args: dict[str, Any] = {} - - field = data.get("id", None) - if field is not None: - args["id"] = field - else: - args["id"] = None - - field = data.get("scim_id", None) - if field is not None: - args["scim_id"] = field - else: - args["scim_id"] = None - - field = data.get("created_at", None) - if field is not None: - args["created_at"] = parser.isoparse(field) if isinstance(field, str) else field - else: - args["created_at"] = None - - field = data.get("expires_at", None) - if field is not None: - args["expires_at"] = parser.isoparse(field) if isinstance(field, str) else field - else: - args["expires_at"] = None - - return ScimToken(**args) - - def unmarshal_CreateScimTokenResponse(data: Any) -> CreateScimTokenResponse: if not isinstance(data, dict): raise TypeError( diff --git a/scaleway-async/scaleway_async/iam/v1alpha1/types.py b/scaleway-async/scaleway_async/iam/v1alpha1/types.py index 40b182a46..1dcfa55dc 100644 --- a/scaleway-async/scaleway_async/iam/v1alpha1/types.py +++ b/scaleway-async/scaleway_async/iam/v1alpha1/types.py @@ -1627,6 +1627,11 @@ class GetSamlCertificateRequest: """ +@dataclass +class GetScimTokenRequest: + scim_token_id: str + + @dataclass class GetUserConnectionsRequest: user_id: str diff --git a/scaleway-async/scaleway_async/instance/v2alpha1/__init__.py b/scaleway-async/scaleway_async/instance/v2alpha1/__init__.py index 6f9013cb4..38d847836 100644 --- a/scaleway-async/scaleway_async/instance/v2alpha1/__init__.py +++ b/scaleway-async/scaleway_async/instance/v2alpha1/__init__.py @@ -45,6 +45,7 @@ from .types import CreateServerRequestCreateVolume from .types import ServerTypeGpuInfo from .types import ServerTypeLimits +from .types import ServerTypeSpotInfo from .types import ServerIP from .types import CreateTemplateRequestPrivateNetworkTemplate from .types import CreateTemplateRequestVolumeTemplate @@ -67,6 +68,7 @@ from .types import ServerPrivateNetworkInterface from .types import ServerPublicNetworkInterface from .types import ServerRDPPassword +from .types import ServerRuntimeInfo from .types import ServerVolume from .types import UpdateServerRequestPublicNetworkInterface from .types import UpdateTemplateRequestUpdatePrivateNetworks @@ -146,6 +148,7 @@ from .types import SetTemplateUserDataRequest from .types import SetUserDataRequest from .types import StartServerRequest +from .types import StartSpotServerRequest from .types import StopAndDeleteServerRequest from .types import StopServerRequest from .types import Template @@ -219,6 +222,7 @@ "CreateServerRequestCreateVolume", "ServerTypeGpuInfo", "ServerTypeLimits", + "ServerTypeSpotInfo", "ServerIP", "CreateTemplateRequestPrivateNetworkTemplate", "CreateTemplateRequestVolumeTemplate", @@ -241,6 +245,7 @@ "ServerPrivateNetworkInterface", "ServerPublicNetworkInterface", "ServerRDPPassword", + "ServerRuntimeInfo", "ServerVolume", "UpdateServerRequestPublicNetworkInterface", "UpdateTemplateRequestUpdatePrivateNetworks", @@ -320,6 +325,7 @@ "SetTemplateUserDataRequest", "SetUserDataRequest", "StartServerRequest", + "StartSpotServerRequest", "StopAndDeleteServerRequest", "StopServerRequest", "Template", diff --git a/scaleway-async/scaleway_async/instance/v2alpha1/api.py b/scaleway-async/scaleway_async/instance/v2alpha1/api.py index 1c8ab43d0..03fde4560 100644 --- a/scaleway-async/scaleway_async/instance/v2alpha1/api.py +++ b/scaleway-async/scaleway_async/instance/v2alpha1/api.py @@ -672,6 +672,40 @@ async def start_server( self._throw_on_error(res) return unmarshal_Server(res.json()) + async def start_spot_server( + self, + *, + server_id: str, + zone: Optional[ScwZone] = None, + ) -> Server: + """ + Start an Instance as Spot. + Spot instances are billed at a discount compared to regular instances. However, they can be interrupted + at any time. + :param server_id: ID of the server to start as spot instance. + :param zone: Zone to target. If none is passed will use default zone from the config. + :return: :class:`Server ` + + Usage: + :: + + result = await api.start_spot_server( + server_id="example", + ) + """ + + param_zone = validate_path_param("zone", zone or self.client.default_zone) + param_server_id = validate_path_param("server_id", server_id) + + res = self._request( + "POST", + f"/instance/v2alpha1/zones/{param_zone}/servers/{param_server_id}/start-spot", + body={}, + ) + + self._throw_on_error(res) + return unmarshal_Server(res.json()) + async def reboot_server( self, *, diff --git a/scaleway-async/scaleway_async/instance/v2alpha1/marshalling.py b/scaleway-async/scaleway_async/instance/v2alpha1/marshalling.py index 6cb9e138a..a97e65325 100644 --- a/scaleway-async/scaleway_async/instance/v2alpha1/marshalling.py +++ b/scaleway-async/scaleway_async/instance/v2alpha1/marshalling.py @@ -44,6 +44,7 @@ ListPrivateNetworkInterfacesResponse, SecurityGroupSummary, ListSecurityGroupsResponse, + ServerTypeSpotInfo, ServerType, ListServerCompatibleTypesResponse, ListServerTypesResponse, @@ -64,6 +65,7 @@ ServerPrivateNetworkInterface, ServerPublicNetworkInterface, ServerRDPPassword, + ServerRuntimeInfo, ServerVolume, Server, CreateTemplateRequestPrivateNetworkTemplate, @@ -1215,6 +1217,23 @@ def unmarshal_ListSecurityGroupsResponse(data: Any) -> ListSecurityGroupsRespons return ListSecurityGroupsResponse(**args) +def unmarshal_ServerTypeSpotInfo(data: Any) -> ServerTypeSpotInfo: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'ServerTypeSpotInfo' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("availability", None) + if field is not None: + args["availability"] = field + else: + args["availability"] = None + + return ServerTypeSpotInfo(**args) + + def unmarshal_ServerType(data: Any) -> ServerType: if not isinstance(data, dict): raise TypeError( @@ -1277,6 +1296,12 @@ def unmarshal_ServerType(data: Any) -> ServerType: else: args["gpu_info"] = None + field = data.get("spot_info", None) + if field is not None: + args["spot_info"] = unmarshal_ServerTypeSpotInfo(field) + else: + args["spot_info"] = None + return ServerType(**args) @@ -2139,6 +2164,23 @@ def unmarshal_ServerRDPPassword(data: Any) -> ServerRDPPassword: return ServerRDPPassword(**args) +def unmarshal_ServerRuntimeInfo(data: Any) -> ServerRuntimeInfo: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'ServerRuntimeInfo' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("spot", None) + if field is not None: + args["spot"] = field + else: + args["spot"] = False + + return ServerRuntimeInfo(**args) + + def unmarshal_ServerVolume(data: Any) -> ServerVolume: if not isinstance(data, dict): raise TypeError( @@ -2306,6 +2348,12 @@ def unmarshal_Server(data: Any) -> Server: else: args["public_network_interface"] = None + field = data.get("runtime_info", None) + if field is not None: + args["runtime_info"] = unmarshal_ServerRuntimeInfo(field) + else: + args["runtime_info"] = None + return Server(**args) diff --git a/scaleway-async/scaleway_async/instance/v2alpha1/types.py b/scaleway-async/scaleway_async/instance/v2alpha1/types.py index 69e0f33fb..071bebc59 100644 --- a/scaleway-async/scaleway_async/instance/v2alpha1/types.py +++ b/scaleway-async/scaleway_async/instance/v2alpha1/types.py @@ -267,6 +267,7 @@ class ServerTypeAvailability(str, Enum, metaclass=StrEnumMeta): AVAILABLE = "available" LOW_STOCK = "low_stock" OUT_OF_STOCK = "out_of_stock" + UNAVAILABLE = "unavailable" def __str__(self) -> str: return str(self.value) @@ -504,6 +505,11 @@ class ServerTypeLimits: """ +@dataclass +class ServerTypeSpotInfo: + availability: ServerTypeAvailability + + @dataclass class ServerIP: id: str @@ -1026,6 +1032,11 @@ class ServerType: GPU information for the server type. """ + spot_info: Optional[ServerTypeSpotInfo] = None + """ + Availability status of the server type as a spot instance. + """ + @dataclass class ServerSummary: @@ -1358,6 +1369,14 @@ class ServerRDPPassword: rdp_ssh_key_id: str +@dataclass +class ServerRuntimeInfo: + spot: bool + """ + True if the Instance is running as Spot. + """ + + @dataclass class ServerVolume: id: str @@ -3113,6 +3132,11 @@ class Server: Public network interface of the server. """ + runtime_info: Optional[ServerRuntimeInfo] = None + """ + Runtime information of this server, only available only when it is running. + """ + @dataclass class SetSecurityGroupRulesRequest: @@ -3247,6 +3271,19 @@ class StartServerRequest: """ +@dataclass +class StartSpotServerRequest: + server_id: str + """ + ID of the server to start as spot instance. + """ + + zone: Optional[ScwZone] = None + """ + Zone to target. If none is passed will use default zone from the config. + """ + + @dataclass class StopAndDeleteServerRequest: server_id: str diff --git a/scaleway-async/scaleway_async/k8s/v1/marshalling.py b/scaleway-async/scaleway_async/k8s/v1/marshalling.py index 4776b0c0c..4fea170ee 100644 --- a/scaleway-async/scaleway_async/k8s/v1/marshalling.py +++ b/scaleway-async/scaleway_async/k8s/v1/marshalling.py @@ -131,14 +131,6 @@ def unmarshal_Version(data: Any) -> Version: else: args["available_cnis"] = [] - field = data.get("available_container_runtimes", None) - if field is not None: - args["available_container_runtimes"] = ( - [Runtime(v) for v in field] if field is not None else None - ) - else: - args["available_container_runtimes"] = [] - field = data.get("available_feature_gates", None) if field is not None: args["available_feature_gates"] = field @@ -498,6 +490,12 @@ def unmarshal_Cluster(data: Any) -> Cluster: else: args["iam_nodes_group_id"] = None + field = data.get("iam_control_plane_application_id", None) + if field is not None: + args["iam_control_plane_application_id"] = field + else: + args["iam_control_plane_application_id"] = None + field = data.get("pod_cidr", None) if field is not None: args["pod_cidr"] = field diff --git a/scaleway-async/scaleway_async/k8s/v1/types.py b/scaleway-async/scaleway_async/k8s/v1/types.py index 1573a53a5..52e1c3f1d 100644 --- a/scaleway-async/scaleway_async/k8s/v1/types.py +++ b/scaleway-async/scaleway_async/k8s/v1/types.py @@ -768,11 +768,6 @@ class Version: Supported Container Network Interface (CNI) plugins for this version. """ - available_container_runtimes: list[Runtime] - """ - Supported container runtimes for this version. - """ - available_feature_gates: list[str] """ Supported feature gates for this version. @@ -906,6 +901,11 @@ class Cluster: IAM group that nodes are members of (this field might be empty during early stage of cluster creation). """ + iam_control_plane_application_id: str + """ + IAM application ID for the control plane (this field might be empty during early stage of cluster creation). + """ + pod_cidr: str """ Subnet used for the Pod CIDR. diff --git a/scaleway-async/scaleway_async/mailbox/v1alpha1/__init__.py b/scaleway-async/scaleway_async/mailbox/v1alpha1/__init__.py index c1e0c619e..394541b5d 100644 --- a/scaleway-async/scaleway_async/mailbox/v1alpha1/__init__.py +++ b/scaleway-async/scaleway_async/mailbox/v1alpha1/__init__.py @@ -8,8 +8,11 @@ from .content import DOMAIN_RECORD_TRANSIENT_STATUSES from .types import DomainStatus from .content import DOMAIN_TRANSIENT_STATUSES +from .types import ForwardingStatus +from .content import FORWARDING_TRANSIENT_STATUSES from .types import ListAliasesRequestOrderBy from .types import ListDomainsRequestOrderBy +from .types import ListForwardingsRequestOrderBy from .types import ListMailboxesRequestOrderBy from .types import MailboxStatus from .content import MAILBOX_TRANSIENT_STATUSES @@ -19,26 +22,36 @@ from .types import DomainRecord from .types import Alias from .types import Domain +from .types import Forwarding from .types import BatchCreateMailboxesRequest from .types import BatchCreateMailboxesResponse from .types import CreateAliasRequest from .types import CreateDomainRequest +from .types import CreateForwardingRequest from .types import DeleteAliasRequest from .types import DeleteDomainRequest +from .types import DeleteForwardingRequest from .types import DeleteMailboxRequest from .types import GetAliasRequest from .types import GetDomainRecordsRequest from .types import GetDomainRecordsResponse from .types import GetDomainRequest +from .types import GetForwardingRequest +from .types import GetMailboxForwardingRequest from .types import GetMailboxRequest from .types import ListAliasesRequest from .types import ListAliasesResponse from .types import ListDomainsRequest from .types import ListDomainsResponse +from .types import ListForwardingsRequest +from .types import ListForwardingsResponse from .types import ListMailboxesRequest from .types import ListMailboxesResponse +from .types import MailboxForwarding from .types import RestoreMailboxRequest from .types import UpdateAliasRequest +from .types import UpdateForwardingRequest +from .types import UpdateMailboxForwardingRequest from .types import UpdateMailboxRequest from .types import ValidateDomainRecordsRequest from .api import MailboxV1Alpha1API @@ -52,8 +65,11 @@ "DOMAIN_RECORD_TRANSIENT_STATUSES", "DomainStatus", "DOMAIN_TRANSIENT_STATUSES", + "ForwardingStatus", + "FORWARDING_TRANSIENT_STATUSES", "ListAliasesRequestOrderBy", "ListDomainsRequestOrderBy", + "ListForwardingsRequestOrderBy", "ListMailboxesRequestOrderBy", "MailboxStatus", "MAILBOX_TRANSIENT_STATUSES", @@ -63,26 +79,36 @@ "DomainRecord", "Alias", "Domain", + "Forwarding", "BatchCreateMailboxesRequest", "BatchCreateMailboxesResponse", "CreateAliasRequest", "CreateDomainRequest", + "CreateForwardingRequest", "DeleteAliasRequest", "DeleteDomainRequest", + "DeleteForwardingRequest", "DeleteMailboxRequest", "GetAliasRequest", "GetDomainRecordsRequest", "GetDomainRecordsResponse", "GetDomainRequest", + "GetForwardingRequest", + "GetMailboxForwardingRequest", "GetMailboxRequest", "ListAliasesRequest", "ListAliasesResponse", "ListDomainsRequest", "ListDomainsResponse", + "ListForwardingsRequest", + "ListForwardingsResponse", "ListMailboxesRequest", "ListMailboxesResponse", + "MailboxForwarding", "RestoreMailboxRequest", "UpdateAliasRequest", + "UpdateForwardingRequest", + "UpdateMailboxForwardingRequest", "UpdateMailboxRequest", "ValidateDomainRecordsRequest", "MailboxV1Alpha1API", diff --git a/scaleway-async/scaleway_async/mailbox/v1alpha1/api.py b/scaleway-async/scaleway_async/mailbox/v1alpha1/api.py index 975c793ae..5fac7b90f 100644 --- a/scaleway-async/scaleway_async/mailbox/v1alpha1/api.py +++ b/scaleway-async/scaleway_async/mailbox/v1alpha1/api.py @@ -5,7 +5,9 @@ from scaleway_core.api import API from scaleway_core.utils import ( + OneOfPossibility, WaitForOptions, + resolve_one_of, validate_path_param, fetch_all_pages_async, wait_for_resource_async, @@ -13,8 +15,10 @@ from .types import ( AliasStatus, DomainStatus, + ForwardingStatus, ListAliasesRequestOrderBy, ListDomainsRequestOrderBy, + ListForwardingsRequestOrderBy, ListMailboxesRequestOrderBy, MailboxStatus, MailboxSubscriptionPeriod, @@ -24,33 +28,46 @@ BatchCreateMailboxesResponse, CreateAliasRequest, CreateDomainRequest, + CreateForwardingRequest, Domain, + Forwarding, GetDomainRecordsResponse, ListAliasesResponse, ListDomainsResponse, + ListForwardingsResponse, ListMailboxesResponse, Mailbox, + MailboxForwarding, UpdateAliasRequest, + UpdateForwardingRequest, + UpdateMailboxForwardingRequest, UpdateMailboxRequest, ) from .content import ( ALIAS_TRANSIENT_STATUSES, DOMAIN_TRANSIENT_STATUSES, + FORWARDING_TRANSIENT_STATUSES, MAILBOX_TRANSIENT_STATUSES, ) from .marshalling import ( unmarshal_Mailbox, unmarshal_Alias, unmarshal_Domain, + unmarshal_Forwarding, unmarshal_BatchCreateMailboxesResponse, unmarshal_GetDomainRecordsResponse, unmarshal_ListAliasesResponse, unmarshal_ListDomainsResponse, + unmarshal_ListForwardingsResponse, unmarshal_ListMailboxesResponse, + unmarshal_MailboxForwarding, marshal_BatchCreateMailboxesRequest, marshal_CreateAliasRequest, marshal_CreateDomainRequest, + marshal_CreateForwardingRequest, marshal_UpdateAliasRequest, + marshal_UpdateForwardingRequest, + marshal_UpdateMailboxForwardingRequest, marshal_UpdateMailboxRequest, ) @@ -856,3 +873,333 @@ async def delete_alias( self._throw_on_error(res) return unmarshal_Alias(res.json()) + + async def create_forwarding( + self, + *, + mailbox_id: str, + email: str, + ) -> Forwarding: + """ + Create a forwarding rule for a mailbox. + All incoming emails to the mailbox will be redirected to the specified destination email address. + A mailbox can have up to 5 forwarding rules. Forwarding to the mailbox's own email address is not allowed. + :param mailbox_id: ID of the mailbox for which to create the forwarding rule. + :param email: Destination email address to which incoming emails will be forwarded. Must not be the same as the mailbox's own email address. + :return: :class:`Forwarding ` + + Usage: + :: + + result = await api.create_forwarding( + mailbox_id="example", + email="example", + ) + """ + + res = self._request( + "POST", + "/mailbox/v1alpha1/forwardings", + body=marshal_CreateForwardingRequest( + CreateForwardingRequest( + mailbox_id=mailbox_id, + email=email, + ), + self.client, + ), + ) + + self._throw_on_error(res) + return unmarshal_Forwarding(res.json()) + + async def list_forwardings( + self, + *, + order_by: Optional[ListForwardingsRequestOrderBy] = None, + page: Optional[int] = None, + page_size: Optional[int] = None, + mailbox_id: Optional[str] = None, + status: Optional[ForwardingStatus] = None, + project_id: Optional[str] = None, + organization_id: Optional[str] = None, + ) -> ListForwardingsResponse: + """ + List forwarding rules in an organization. + The return list can be filtered with request parameters. + :param order_by: Order forwardings by specific criteria. + :param page: Requested page number. Value must be greater or equal to 1. + :param page_size: Requested page size. Value must be between 1 and 100. + :param mailbox_id: (Optional) ID of the mailbox for which to list forwarding rules. + :param status: (Optional) Filter forwarding rules by their status. + :param project_id: ID of the Project to filter on. + One-Of ('scope'): at most one of 'project_id', 'organization_id' could be set. + :param organization_id: ID of the Organization to filter on. + One-Of ('scope'): at most one of 'project_id', 'organization_id' could be set. + :return: :class:`ListForwardingsResponse ` + + Usage: + :: + + result = await api.list_forwardings() + """ + + res = self._request( + "GET", + "/mailbox/v1alpha1/forwardings", + params={ + "mailbox_id": mailbox_id, + "order_by": order_by, + "page": page, + "page_size": page_size or self.client.default_page_size, + "status": status, + **resolve_one_of( + [ + OneOfPossibility("organization_id", organization_id), + OneOfPossibility("project_id", project_id), + ] + ), + }, + ) + + self._throw_on_error(res) + return unmarshal_ListForwardingsResponse(res.json()) + + async def list_forwardings_all( + self, + *, + order_by: Optional[ListForwardingsRequestOrderBy] = None, + page: Optional[int] = None, + page_size: Optional[int] = None, + mailbox_id: Optional[str] = None, + status: Optional[ForwardingStatus] = None, + project_id: Optional[str] = None, + organization_id: Optional[str] = None, + ) -> list[Forwarding]: + """ + List forwarding rules in an organization. + The return list can be filtered with request parameters. + :param order_by: Order forwardings by specific criteria. + :param page: Requested page number. Value must be greater or equal to 1. + :param page_size: Requested page size. Value must be between 1 and 100. + :param mailbox_id: (Optional) ID of the mailbox for which to list forwarding rules. + :param status: (Optional) Filter forwarding rules by their status. + :param project_id: ID of the Project to filter on. + One-Of ('scope'): at most one of 'project_id', 'organization_id' could be set. + :param organization_id: ID of the Organization to filter on. + One-Of ('scope'): at most one of 'project_id', 'organization_id' could be set. + :return: :class:`list[Forwarding] ` + + Usage: + :: + + result = await api.list_forwardings_all() + """ + + return await fetch_all_pages_async( + type=ListForwardingsResponse, + key="forwardings", + fetcher=self.list_forwardings, + args={ + "order_by": order_by, + "page": page, + "page_size": page_size, + "mailbox_id": mailbox_id, + "status": status, + "project_id": project_id, + "organization_id": organization_id, + }, + ) + + async def get_forwarding( + self, + *, + forwarding_id: str, + ) -> Forwarding: + """ + Get a forwarding rule by its ID. + :param forwarding_id: ID of the forwarding rule to get. + :return: :class:`Forwarding ` + + Usage: + :: + + result = await api.get_forwarding( + forwarding_id="example", + ) + """ + + param_forwarding_id = validate_path_param("forwarding_id", forwarding_id) + + res = self._request( + "GET", + f"/mailbox/v1alpha1/forwardings/{param_forwarding_id}", + ) + + self._throw_on_error(res) + return unmarshal_Forwarding(res.json()) + + async def wait_for_forwarding( + self, + *, + forwarding_id: str, + options: Optional[ + WaitForOptions[Forwarding, Union[bool, Awaitable[bool]]] + ] = None, + ) -> Forwarding: + """ + Get a forwarding rule by its ID. + :param forwarding_id: ID of the forwarding rule to get. + :return: :class:`Forwarding ` + + Usage: + :: + + result = await api.get_forwarding( + forwarding_id="example", + ) + """ + + if not options: + options = WaitForOptions() + + if not options.stop: + options.stop = lambda res: res.status not in FORWARDING_TRANSIENT_STATUSES + + return await wait_for_resource_async( + fetcher=self.get_forwarding, + options=options, + args={ + "forwarding_id": forwarding_id, + }, + ) + + async def update_forwarding( + self, + *, + forwarding_id: str, + email: Optional[str] = None, + ) -> Forwarding: + """ + Update a forwarding rule's destination email address by its ID. + :param forwarding_id: ID of the forwarding rule to update. + :param email: (Optional) New destination email address for the forwarding rule. Must not be the same as the mailbox's own email address. + :return: :class:`Forwarding ` + + Usage: + :: + + result = await api.update_forwarding( + forwarding_id="example", + ) + """ + + param_forwarding_id = validate_path_param("forwarding_id", forwarding_id) + + res = self._request( + "PATCH", + f"/mailbox/v1alpha1/forwardings/{param_forwarding_id}", + body=marshal_UpdateForwardingRequest( + UpdateForwardingRequest( + forwarding_id=forwarding_id, + email=email, + ), + self.client, + ), + ) + + self._throw_on_error(res) + return unmarshal_Forwarding(res.json()) + + async def delete_forwarding( + self, + *, + forwarding_id: str, + ) -> Forwarding: + """ + Delete a forwarding rule by its ID. + :param forwarding_id: ID of the forwarding rule to delete. + :return: :class:`Forwarding ` + + Usage: + :: + + result = await api.delete_forwarding( + forwarding_id="example", + ) + """ + + param_forwarding_id = validate_path_param("forwarding_id", forwarding_id) + + res = self._request( + "DELETE", + f"/mailbox/v1alpha1/forwardings/{param_forwarding_id}", + ) + + self._throw_on_error(res) + return unmarshal_Forwarding(res.json()) + + async def get_mailbox_forwarding( + self, + *, + mailbox_id: str, + ) -> MailboxForwarding: + """ + :param mailbox_id: ID of the mailbox to get the forwarding settings for. + :return: :class:`MailboxForwarding ` + + Usage: + :: + + result = await api.get_mailbox_forwarding( + mailbox_id="example", + ) + """ + + param_mailbox_id = validate_path_param("mailbox_id", mailbox_id) + + res = self._request( + "GET", + f"/mailbox/v1alpha1/mailboxes/{param_mailbox_id}/forwarding", + ) + + self._throw_on_error(res) + return unmarshal_MailboxForwarding(res.json()) + + async def update_mailbox_forwarding( + self, + *, + mailbox_id: str, + keep_copy: Optional[bool] = None, + enabled: Optional[bool] = None, + ) -> MailboxForwarding: + """ + :param mailbox_id: ID of the mailbox to update the forwarding settings for. + :param keep_copy: (Optional) Whether to keep a copy of forwarded emails in the local mailbox. + :param enabled: (Optional) Enable or disable forwarding for the mailbox. + :return: :class:`MailboxForwarding ` + + Usage: + :: + + result = await api.update_mailbox_forwarding( + mailbox_id="example", + ) + """ + + param_mailbox_id = validate_path_param("mailbox_id", mailbox_id) + + res = self._request( + "PATCH", + f"/mailbox/v1alpha1/mailboxes/{param_mailbox_id}/forwarding", + body=marshal_UpdateMailboxForwardingRequest( + UpdateMailboxForwardingRequest( + mailbox_id=mailbox_id, + keep_copy=keep_copy, + enabled=enabled, + ), + self.client, + ), + ) + + self._throw_on_error(res) + return unmarshal_MailboxForwarding(res.json()) diff --git a/scaleway-async/scaleway_async/mailbox/v1alpha1/content.py b/scaleway-async/scaleway_async/mailbox/v1alpha1/content.py index ae7df61bf..a0a458b38 100644 --- a/scaleway-async/scaleway_async/mailbox/v1alpha1/content.py +++ b/scaleway-async/scaleway_async/mailbox/v1alpha1/content.py @@ -5,6 +5,7 @@ AliasStatus, DomainRecordStatus, DomainStatus, + ForwardingStatus, MailboxStatus, ) @@ -30,6 +31,14 @@ """ Lists transient statutes of the enum :class:`DomainStatus `. """ +FORWARDING_TRANSIENT_STATUSES: list[ForwardingStatus] = [ + ForwardingStatus.PROVISIONING, + ForwardingStatus.UPDATING, + ForwardingStatus.DELETING, +] +""" +Lists transient statutes of the enum :class:`ForwardingStatus `. +""" MAILBOX_TRANSIENT_STATUSES: list[MailboxStatus] = [ MailboxStatus.CREATING, MailboxStatus.WAITING_PAYMENT, diff --git a/scaleway-async/scaleway_async/mailbox/v1alpha1/marshalling.py b/scaleway-async/scaleway_async/mailbox/v1alpha1/marshalling.py index 76878a551..a81f5064a 100644 --- a/scaleway-async/scaleway_async/mailbox/v1alpha1/marshalling.py +++ b/scaleway-async/scaleway_async/mailbox/v1alpha1/marshalling.py @@ -11,22 +11,29 @@ DomainRecordLevel, DomainRecordStatus, DomainStatus, + ForwardingStatus, MailboxStatus, MailboxSubscriptionPeriod, Mailbox, Alias, Domain, + Forwarding, BatchCreateMailboxesResponse, DomainRecord, GetDomainRecordsResponse, ListAliasesResponse, ListDomainsResponse, + ListForwardingsResponse, ListMailboxesResponse, + MailboxForwarding, BatchCreateMailboxesRequestMailboxParameters, BatchCreateMailboxesRequest, CreateAliasRequest, CreateDomainRequest, + CreateForwardingRequest, UpdateAliasRequest, + UpdateForwardingRequest, + UpdateMailboxForwardingRequest, UpdateMailboxRequest, ) @@ -266,6 +273,53 @@ def unmarshal_Domain(data: Any) -> Domain: return Domain(**args) +def unmarshal_Forwarding(data: Any) -> Forwarding: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'Forwarding' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("id", None) + if field is not None: + args["id"] = field + else: + args["id"] = None + + field = data.get("mailbox_id", None) + if field is not None: + args["mailbox_id"] = field + else: + args["mailbox_id"] = None + + field = data.get("email", None) + if field is not None: + args["email"] = field + else: + args["email"] = None + + field = data.get("status", None) + if field is not None: + args["status"] = field + else: + args["status"] = ForwardingStatus.UNKNOWN_STATUS + + field = data.get("created_at", None) + if field is not None: + args["created_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["created_at"] = None + + field = data.get("updated_at", None) + if field is not None: + args["updated_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["updated_at"] = None + + return Forwarding(**args) + + def unmarshal_BatchCreateMailboxesResponse(data: Any) -> BatchCreateMailboxesResponse: if not isinstance(data, dict): raise TypeError( @@ -489,6 +543,31 @@ def unmarshal_ListDomainsResponse(data: Any) -> ListDomainsResponse: return ListDomainsResponse(**args) +def unmarshal_ListForwardingsResponse(data: Any) -> ListForwardingsResponse: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'ListForwardingsResponse' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("total_count", None) + if field is not None: + args["total_count"] = field + else: + args["total_count"] = 0 + + field = data.get("forwardings", None) + if field is not None: + args["forwardings"] = ( + [unmarshal_Forwarding(v) for v in field] if field is not None else None + ) + else: + args["forwardings"] = [] + + return ListForwardingsResponse(**args) + + def unmarshal_ListMailboxesResponse(data: Any) -> ListMailboxesResponse: if not isinstance(data, dict): raise TypeError( @@ -514,6 +593,47 @@ def unmarshal_ListMailboxesResponse(data: Any) -> ListMailboxesResponse: return ListMailboxesResponse(**args) +def unmarshal_MailboxForwarding(data: Any) -> MailboxForwarding: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'MailboxForwarding' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("mailbox_id", None) + if field is not None: + args["mailbox_id"] = field + else: + args["mailbox_id"] = None + + field = data.get("keep_copy", None) + if field is not None: + args["keep_copy"] = field + else: + args["keep_copy"] = False + + field = data.get("enabled", None) + if field is not None: + args["enabled"] = field + else: + args["enabled"] = False + + field = data.get("created_at", None) + if field is not None: + args["created_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["created_at"] = None + + field = data.get("updated_at", None) + if field is not None: + args["updated_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["updated_at"] = None + + return MailboxForwarding(**args) + + def marshal_BatchCreateMailboxesRequestMailboxParameters( request: BatchCreateMailboxesRequestMailboxParameters, defaults: ProfileDefaults, @@ -585,6 +705,21 @@ def marshal_CreateDomainRequest( return output +def marshal_CreateForwardingRequest( + request: CreateForwardingRequest, + defaults: ProfileDefaults, +) -> dict[str, Any]: + output: dict[str, Any] = {} + + if request.mailbox_id is not None: + output["mailbox_id"] = request.mailbox_id + + if request.email is not None: + output["email"] = request.email + + return output + + def marshal_UpdateAliasRequest( request: UpdateAliasRequest, defaults: ProfileDefaults, @@ -597,6 +732,33 @@ def marshal_UpdateAliasRequest( return output +def marshal_UpdateForwardingRequest( + request: UpdateForwardingRequest, + defaults: ProfileDefaults, +) -> dict[str, Any]: + output: dict[str, Any] = {} + + if request.email is not None: + output["email"] = request.email + + return output + + +def marshal_UpdateMailboxForwardingRequest( + request: UpdateMailboxForwardingRequest, + defaults: ProfileDefaults, +) -> dict[str, Any]: + output: dict[str, Any] = {} + + if request.keep_copy is not None: + output["keep_copy"] = request.keep_copy + + if request.enabled is not None: + output["enabled"] = request.enabled + + return output + + def marshal_UpdateMailboxRequest( request: UpdateMailboxRequest, defaults: ProfileDefaults, diff --git a/scaleway-async/scaleway_async/mailbox/v1alpha1/types.py b/scaleway-async/scaleway_async/mailbox/v1alpha1/types.py index e06a89ba9..c3cf27e53 100644 --- a/scaleway-async/scaleway_async/mailbox/v1alpha1/types.py +++ b/scaleway-async/scaleway_async/mailbox/v1alpha1/types.py @@ -68,6 +68,17 @@ def __str__(self) -> str: return str(self.value) +class ForwardingStatus(str, Enum, metaclass=StrEnumMeta): + UNKNOWN_STATUS = "unknown_status" + PROVISIONING = "provisioning" + READY = "ready" + UPDATING = "updating" + DELETING = "deleting" + + def __str__(self) -> str: + return str(self.value) + + class ListAliasesRequestOrderBy(str, Enum, metaclass=StrEnumMeta): CREATED_AT_DESC = "created_at_desc" CREATED_AT_ASC = "created_at_asc" @@ -94,6 +105,18 @@ def __str__(self) -> str: return str(self.value) +class ListForwardingsRequestOrderBy(str, Enum, metaclass=StrEnumMeta): + CREATED_AT_DESC = "created_at_desc" + CREATED_AT_ASC = "created_at_asc" + UPDATED_AT_DESC = "updated_at_desc" + UPDATED_AT_ASC = "updated_at_asc" + EMAIL_DESC = "email_desc" + EMAIL_ASC = "email_asc" + + def __str__(self) -> str: + return str(self.value) + + class ListMailboxesRequestOrderBy(str, Enum, metaclass=StrEnumMeta): CREATED_AT_DESC = "created_at_desc" CREATED_AT_ASC = "created_at_asc" @@ -361,6 +384,39 @@ class Domain: """ +@dataclass +class Forwarding: + id: str + """ + Unique identifier of the forwarding rule. + """ + + mailbox_id: str + """ + ID of the mailbox to which the forwarding rule belongs. + """ + + email: str + """ + Destination email address to which incoming emails are forwarded. + """ + + status: ForwardingStatus + """ + Status of the forwarding rule. + """ + + created_at: Optional[datetime] = None + """ + Date and time of forwarding rule creation. + """ + + updated_at: Optional[datetime] = None + """ + Date and time when the forwarding rule was last updated. + """ + + @dataclass class BatchCreateMailboxesRequest: domain_id: str @@ -422,6 +478,19 @@ class CreateDomainRequest: """ +@dataclass +class CreateForwardingRequest: + mailbox_id: str + """ + ID of the mailbox for which to create the forwarding rule. + """ + + email: str + """ + Destination email address to which incoming emails will be forwarded. Must not be the same as the mailbox's own email address. + """ + + @dataclass class DeleteAliasRequest: alias_id: str @@ -438,6 +507,14 @@ class DeleteDomainRequest: """ +@dataclass +class DeleteForwardingRequest: + forwarding_id: str + """ + ID of the forwarding rule to delete. + """ + + @dataclass class DeleteMailboxRequest: mailbox_id: str @@ -533,6 +610,22 @@ class GetDomainRequest: """ +@dataclass +class GetForwardingRequest: + forwarding_id: str + """ + ID of the forwarding rule to get. + """ + + +@dataclass +class GetMailboxForwardingRequest: + mailbox_id: str + """ + ID of the mailbox to get the forwarding settings for. + """ + + @dataclass class GetMailboxRequest: mailbox_id: str @@ -612,6 +705,53 @@ class ListDomainsResponse: """ +@dataclass +class ListForwardingsRequest: + order_by: Optional[ListForwardingsRequestOrderBy] = ( + ListForwardingsRequestOrderBy.CREATED_AT_DESC + ) + """ + Order forwardings by specific criteria. + """ + + page: Optional[int] = 0 + """ + Requested page number. Value must be greater or equal to 1. + """ + + page_size: Optional[int] = 0 + """ + Requested page size. Value must be between 1 and 100. + """ + + mailbox_id: Optional[str] = None + """ + (Optional) ID of the mailbox for which to list forwarding rules. + """ + + status: Optional[ForwardingStatus] = ForwardingStatus.UNKNOWN_STATUS + """ + (Optional) Filter forwarding rules by their status. + """ + + project_id: Optional[str] = None + + organization_id: Optional[str] = None + + +@dataclass +class ListForwardingsResponse: + total_count: int + """ + Number of forwarding rules that match the request (without pagination). + """ + + forwardings: list[Forwarding] + """ + Single page of forwarding rules matching the requested criteria. + """ + + @dataclass class ListMailboxesRequest: order_by: Optional[ListMailboxesRequestOrderBy] = ( @@ -665,6 +805,34 @@ class ListMailboxesResponse: """ +@dataclass +class MailboxForwarding: + mailbox_id: str + """ + ID of the mailbox to which the forwarding settings belong. + """ + + keep_copy: bool + """ + Whether to keep a copy of forwarded emails in the local mailbox. + """ + + enabled: bool + """ + Whether forwarding is enabled for the mailbox. When disabled, no emails are forwarded. + """ + + created_at: Optional[datetime] = None + """ + Date and time of the forwarding settings creation. + """ + + updated_at: Optional[datetime] = None + """ + Date and time when the forwarding settings were last updated. + """ + + @dataclass class RestoreMailboxRequest: mailbox_id: str @@ -686,6 +854,37 @@ class UpdateAliasRequest: """ +@dataclass +class UpdateForwardingRequest: + forwarding_id: str + """ + ID of the forwarding rule to update. + """ + + email: Optional[str] = None + """ + (Optional) New destination email address for the forwarding rule. Must not be the same as the mailbox's own email address. + """ + + +@dataclass +class UpdateMailboxForwardingRequest: + mailbox_id: str + """ + ID of the mailbox to update the forwarding settings for. + """ + + keep_copy: Optional[bool] = False + """ + (Optional) Whether to keep a copy of forwarded emails in the local mailbox. + """ + + enabled: Optional[bool] = False + """ + (Optional) Enable or disable forwarding for the mailbox. + """ + + @dataclass class UpdateMailboxRequest: mailbox_id: str diff --git a/scaleway/scaleway/iam/v1alpha1/__init__.py b/scaleway/scaleway/iam/v1alpha1/__init__.py index 052ad9df3..d4a0d8366 100644 --- a/scaleway/scaleway/iam/v1alpha1/__init__.py +++ b/scaleway/scaleway/iam/v1alpha1/__init__.py @@ -90,6 +90,7 @@ from .types import GetQuotumRequest from .types import GetSSHKeyRequest from .types import GetSamlCertificateRequest +from .types import GetScimTokenRequest from .types import GetUserConnectionsRequest from .types import GetUserConnectionsResponse from .types import GetUserRequest @@ -250,6 +251,7 @@ "GetQuotumRequest", "GetSSHKeyRequest", "GetSamlCertificateRequest", + "GetScimTokenRequest", "GetUserConnectionsRequest", "GetUserConnectionsResponse", "GetUserRequest", diff --git a/scaleway/scaleway/iam/v1alpha1/api.py b/scaleway/scaleway/iam/v1alpha1/api.py index daf1e4910..8c8614fe7 100644 --- a/scaleway/scaleway/iam/v1alpha1/api.py +++ b/scaleway/scaleway/iam/v1alpha1/api.py @@ -112,6 +112,7 @@ ) from .marshalling import ( unmarshal_JWT, + unmarshal_ScimToken, unmarshal_APIKey, unmarshal_Application, unmarshal_Group, @@ -3590,6 +3591,33 @@ def delete_scim_token( self._throw_on_error(res) + def get_scim_token( + self, + *, + scim_token_id: str, + ) -> ScimToken: + """ + :param scim_token_id: + :return: :class:`ScimToken ` + + Usage: + :: + + result = api.get_scim_token( + scim_token_id="example", + ) + """ + + param_scim_token_id = validate_path_param("scim_token_id", scim_token_id) + + res = self._request( + "GET", + f"/iam/v1alpha1/scim-tokens/{param_scim_token_id}", + ) + + self._throw_on_error(res) + return unmarshal_ScimToken(res.json()) + def start_user_web_authn_registration( self, *, diff --git a/scaleway/scaleway/iam/v1alpha1/marshalling.py b/scaleway/scaleway/iam/v1alpha1/marshalling.py index 530ac4f4d..e30b457cd 100644 --- a/scaleway/scaleway/iam/v1alpha1/marshalling.py +++ b/scaleway/scaleway/iam/v1alpha1/marshalling.py @@ -23,6 +23,7 @@ SamlStatus, UserType, JWT, + ScimToken, APIKey, Application, Group, @@ -34,7 +35,6 @@ SamlCertificate, WebAuthnAuthenticator, User, - ScimToken, CreateScimTokenResponse, EncodedJWT, FinishUserWebAuthnRegistrationResponse, @@ -172,6 +172,41 @@ def unmarshal_JWT(data: Any) -> JWT: return JWT(**args) +def unmarshal_ScimToken(data: Any) -> ScimToken: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'ScimToken' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("id", None) + if field is not None: + args["id"] = field + else: + args["id"] = None + + field = data.get("scim_id", None) + if field is not None: + args["scim_id"] = field + else: + args["scim_id"] = None + + field = data.get("created_at", None) + if field is not None: + args["created_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["created_at"] = None + + field = data.get("expires_at", None) + if field is not None: + args["expires_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["expires_at"] = None + + return ScimToken(**args) + + def unmarshal_APIKey(data: Any) -> APIKey: if not isinstance(data, dict): raise TypeError( @@ -1037,41 +1072,6 @@ def unmarshal_User(data: Any) -> User: return User(**args) -def unmarshal_ScimToken(data: Any) -> ScimToken: - if not isinstance(data, dict): - raise TypeError( - "Unmarshalling the type 'ScimToken' failed as data isn't a dictionary." - ) - - args: dict[str, Any] = {} - - field = data.get("id", None) - if field is not None: - args["id"] = field - else: - args["id"] = None - - field = data.get("scim_id", None) - if field is not None: - args["scim_id"] = field - else: - args["scim_id"] = None - - field = data.get("created_at", None) - if field is not None: - args["created_at"] = parser.isoparse(field) if isinstance(field, str) else field - else: - args["created_at"] = None - - field = data.get("expires_at", None) - if field is not None: - args["expires_at"] = parser.isoparse(field) if isinstance(field, str) else field - else: - args["expires_at"] = None - - return ScimToken(**args) - - def unmarshal_CreateScimTokenResponse(data: Any) -> CreateScimTokenResponse: if not isinstance(data, dict): raise TypeError( diff --git a/scaleway/scaleway/iam/v1alpha1/types.py b/scaleway/scaleway/iam/v1alpha1/types.py index 40b182a46..1dcfa55dc 100644 --- a/scaleway/scaleway/iam/v1alpha1/types.py +++ b/scaleway/scaleway/iam/v1alpha1/types.py @@ -1627,6 +1627,11 @@ class GetSamlCertificateRequest: """ +@dataclass +class GetScimTokenRequest: + scim_token_id: str + + @dataclass class GetUserConnectionsRequest: user_id: str diff --git a/scaleway/scaleway/instance/v2alpha1/__init__.py b/scaleway/scaleway/instance/v2alpha1/__init__.py index 6f9013cb4..38d847836 100644 --- a/scaleway/scaleway/instance/v2alpha1/__init__.py +++ b/scaleway/scaleway/instance/v2alpha1/__init__.py @@ -45,6 +45,7 @@ from .types import CreateServerRequestCreateVolume from .types import ServerTypeGpuInfo from .types import ServerTypeLimits +from .types import ServerTypeSpotInfo from .types import ServerIP from .types import CreateTemplateRequestPrivateNetworkTemplate from .types import CreateTemplateRequestVolumeTemplate @@ -67,6 +68,7 @@ from .types import ServerPrivateNetworkInterface from .types import ServerPublicNetworkInterface from .types import ServerRDPPassword +from .types import ServerRuntimeInfo from .types import ServerVolume from .types import UpdateServerRequestPublicNetworkInterface from .types import UpdateTemplateRequestUpdatePrivateNetworks @@ -146,6 +148,7 @@ from .types import SetTemplateUserDataRequest from .types import SetUserDataRequest from .types import StartServerRequest +from .types import StartSpotServerRequest from .types import StopAndDeleteServerRequest from .types import StopServerRequest from .types import Template @@ -219,6 +222,7 @@ "CreateServerRequestCreateVolume", "ServerTypeGpuInfo", "ServerTypeLimits", + "ServerTypeSpotInfo", "ServerIP", "CreateTemplateRequestPrivateNetworkTemplate", "CreateTemplateRequestVolumeTemplate", @@ -241,6 +245,7 @@ "ServerPrivateNetworkInterface", "ServerPublicNetworkInterface", "ServerRDPPassword", + "ServerRuntimeInfo", "ServerVolume", "UpdateServerRequestPublicNetworkInterface", "UpdateTemplateRequestUpdatePrivateNetworks", @@ -320,6 +325,7 @@ "SetTemplateUserDataRequest", "SetUserDataRequest", "StartServerRequest", + "StartSpotServerRequest", "StopAndDeleteServerRequest", "StopServerRequest", "Template", diff --git a/scaleway/scaleway/instance/v2alpha1/api.py b/scaleway/scaleway/instance/v2alpha1/api.py index 4b04c81fc..0ab39d285 100644 --- a/scaleway/scaleway/instance/v2alpha1/api.py +++ b/scaleway/scaleway/instance/v2alpha1/api.py @@ -672,6 +672,40 @@ def start_server( self._throw_on_error(res) return unmarshal_Server(res.json()) + def start_spot_server( + self, + *, + server_id: str, + zone: Optional[ScwZone] = None, + ) -> Server: + """ + Start an Instance as Spot. + Spot instances are billed at a discount compared to regular instances. However, they can be interrupted + at any time. + :param server_id: ID of the server to start as spot instance. + :param zone: Zone to target. If none is passed will use default zone from the config. + :return: :class:`Server ` + + Usage: + :: + + result = api.start_spot_server( + server_id="example", + ) + """ + + param_zone = validate_path_param("zone", zone or self.client.default_zone) + param_server_id = validate_path_param("server_id", server_id) + + res = self._request( + "POST", + f"/instance/v2alpha1/zones/{param_zone}/servers/{param_server_id}/start-spot", + body={}, + ) + + self._throw_on_error(res) + return unmarshal_Server(res.json()) + def reboot_server( self, *, diff --git a/scaleway/scaleway/instance/v2alpha1/marshalling.py b/scaleway/scaleway/instance/v2alpha1/marshalling.py index 6cb9e138a..a97e65325 100644 --- a/scaleway/scaleway/instance/v2alpha1/marshalling.py +++ b/scaleway/scaleway/instance/v2alpha1/marshalling.py @@ -44,6 +44,7 @@ ListPrivateNetworkInterfacesResponse, SecurityGroupSummary, ListSecurityGroupsResponse, + ServerTypeSpotInfo, ServerType, ListServerCompatibleTypesResponse, ListServerTypesResponse, @@ -64,6 +65,7 @@ ServerPrivateNetworkInterface, ServerPublicNetworkInterface, ServerRDPPassword, + ServerRuntimeInfo, ServerVolume, Server, CreateTemplateRequestPrivateNetworkTemplate, @@ -1215,6 +1217,23 @@ def unmarshal_ListSecurityGroupsResponse(data: Any) -> ListSecurityGroupsRespons return ListSecurityGroupsResponse(**args) +def unmarshal_ServerTypeSpotInfo(data: Any) -> ServerTypeSpotInfo: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'ServerTypeSpotInfo' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("availability", None) + if field is not None: + args["availability"] = field + else: + args["availability"] = None + + return ServerTypeSpotInfo(**args) + + def unmarshal_ServerType(data: Any) -> ServerType: if not isinstance(data, dict): raise TypeError( @@ -1277,6 +1296,12 @@ def unmarshal_ServerType(data: Any) -> ServerType: else: args["gpu_info"] = None + field = data.get("spot_info", None) + if field is not None: + args["spot_info"] = unmarshal_ServerTypeSpotInfo(field) + else: + args["spot_info"] = None + return ServerType(**args) @@ -2139,6 +2164,23 @@ def unmarshal_ServerRDPPassword(data: Any) -> ServerRDPPassword: return ServerRDPPassword(**args) +def unmarshal_ServerRuntimeInfo(data: Any) -> ServerRuntimeInfo: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'ServerRuntimeInfo' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("spot", None) + if field is not None: + args["spot"] = field + else: + args["spot"] = False + + return ServerRuntimeInfo(**args) + + def unmarshal_ServerVolume(data: Any) -> ServerVolume: if not isinstance(data, dict): raise TypeError( @@ -2306,6 +2348,12 @@ def unmarshal_Server(data: Any) -> Server: else: args["public_network_interface"] = None + field = data.get("runtime_info", None) + if field is not None: + args["runtime_info"] = unmarshal_ServerRuntimeInfo(field) + else: + args["runtime_info"] = None + return Server(**args) diff --git a/scaleway/scaleway/instance/v2alpha1/types.py b/scaleway/scaleway/instance/v2alpha1/types.py index 69e0f33fb..071bebc59 100644 --- a/scaleway/scaleway/instance/v2alpha1/types.py +++ b/scaleway/scaleway/instance/v2alpha1/types.py @@ -267,6 +267,7 @@ class ServerTypeAvailability(str, Enum, metaclass=StrEnumMeta): AVAILABLE = "available" LOW_STOCK = "low_stock" OUT_OF_STOCK = "out_of_stock" + UNAVAILABLE = "unavailable" def __str__(self) -> str: return str(self.value) @@ -504,6 +505,11 @@ class ServerTypeLimits: """ +@dataclass +class ServerTypeSpotInfo: + availability: ServerTypeAvailability + + @dataclass class ServerIP: id: str @@ -1026,6 +1032,11 @@ class ServerType: GPU information for the server type. """ + spot_info: Optional[ServerTypeSpotInfo] = None + """ + Availability status of the server type as a spot instance. + """ + @dataclass class ServerSummary: @@ -1358,6 +1369,14 @@ class ServerRDPPassword: rdp_ssh_key_id: str +@dataclass +class ServerRuntimeInfo: + spot: bool + """ + True if the Instance is running as Spot. + """ + + @dataclass class ServerVolume: id: str @@ -3113,6 +3132,11 @@ class Server: Public network interface of the server. """ + runtime_info: Optional[ServerRuntimeInfo] = None + """ + Runtime information of this server, only available only when it is running. + """ + @dataclass class SetSecurityGroupRulesRequest: @@ -3247,6 +3271,19 @@ class StartServerRequest: """ +@dataclass +class StartSpotServerRequest: + server_id: str + """ + ID of the server to start as spot instance. + """ + + zone: Optional[ScwZone] = None + """ + Zone to target. If none is passed will use default zone from the config. + """ + + @dataclass class StopAndDeleteServerRequest: server_id: str diff --git a/scaleway/scaleway/k8s/v1/marshalling.py b/scaleway/scaleway/k8s/v1/marshalling.py index 4776b0c0c..4fea170ee 100644 --- a/scaleway/scaleway/k8s/v1/marshalling.py +++ b/scaleway/scaleway/k8s/v1/marshalling.py @@ -131,14 +131,6 @@ def unmarshal_Version(data: Any) -> Version: else: args["available_cnis"] = [] - field = data.get("available_container_runtimes", None) - if field is not None: - args["available_container_runtimes"] = ( - [Runtime(v) for v in field] if field is not None else None - ) - else: - args["available_container_runtimes"] = [] - field = data.get("available_feature_gates", None) if field is not None: args["available_feature_gates"] = field @@ -498,6 +490,12 @@ def unmarshal_Cluster(data: Any) -> Cluster: else: args["iam_nodes_group_id"] = None + field = data.get("iam_control_plane_application_id", None) + if field is not None: + args["iam_control_plane_application_id"] = field + else: + args["iam_control_plane_application_id"] = None + field = data.get("pod_cidr", None) if field is not None: args["pod_cidr"] = field diff --git a/scaleway/scaleway/k8s/v1/types.py b/scaleway/scaleway/k8s/v1/types.py index 1573a53a5..52e1c3f1d 100644 --- a/scaleway/scaleway/k8s/v1/types.py +++ b/scaleway/scaleway/k8s/v1/types.py @@ -768,11 +768,6 @@ class Version: Supported Container Network Interface (CNI) plugins for this version. """ - available_container_runtimes: list[Runtime] - """ - Supported container runtimes for this version. - """ - available_feature_gates: list[str] """ Supported feature gates for this version. @@ -906,6 +901,11 @@ class Cluster: IAM group that nodes are members of (this field might be empty during early stage of cluster creation). """ + iam_control_plane_application_id: str + """ + IAM application ID for the control plane (this field might be empty during early stage of cluster creation). + """ + pod_cidr: str """ Subnet used for the Pod CIDR. diff --git a/scaleway/scaleway/mailbox/v1alpha1/__init__.py b/scaleway/scaleway/mailbox/v1alpha1/__init__.py index c1e0c619e..394541b5d 100644 --- a/scaleway/scaleway/mailbox/v1alpha1/__init__.py +++ b/scaleway/scaleway/mailbox/v1alpha1/__init__.py @@ -8,8 +8,11 @@ from .content import DOMAIN_RECORD_TRANSIENT_STATUSES from .types import DomainStatus from .content import DOMAIN_TRANSIENT_STATUSES +from .types import ForwardingStatus +from .content import FORWARDING_TRANSIENT_STATUSES from .types import ListAliasesRequestOrderBy from .types import ListDomainsRequestOrderBy +from .types import ListForwardingsRequestOrderBy from .types import ListMailboxesRequestOrderBy from .types import MailboxStatus from .content import MAILBOX_TRANSIENT_STATUSES @@ -19,26 +22,36 @@ from .types import DomainRecord from .types import Alias from .types import Domain +from .types import Forwarding from .types import BatchCreateMailboxesRequest from .types import BatchCreateMailboxesResponse from .types import CreateAliasRequest from .types import CreateDomainRequest +from .types import CreateForwardingRequest from .types import DeleteAliasRequest from .types import DeleteDomainRequest +from .types import DeleteForwardingRequest from .types import DeleteMailboxRequest from .types import GetAliasRequest from .types import GetDomainRecordsRequest from .types import GetDomainRecordsResponse from .types import GetDomainRequest +from .types import GetForwardingRequest +from .types import GetMailboxForwardingRequest from .types import GetMailboxRequest from .types import ListAliasesRequest from .types import ListAliasesResponse from .types import ListDomainsRequest from .types import ListDomainsResponse +from .types import ListForwardingsRequest +from .types import ListForwardingsResponse from .types import ListMailboxesRequest from .types import ListMailboxesResponse +from .types import MailboxForwarding from .types import RestoreMailboxRequest from .types import UpdateAliasRequest +from .types import UpdateForwardingRequest +from .types import UpdateMailboxForwardingRequest from .types import UpdateMailboxRequest from .types import ValidateDomainRecordsRequest from .api import MailboxV1Alpha1API @@ -52,8 +65,11 @@ "DOMAIN_RECORD_TRANSIENT_STATUSES", "DomainStatus", "DOMAIN_TRANSIENT_STATUSES", + "ForwardingStatus", + "FORWARDING_TRANSIENT_STATUSES", "ListAliasesRequestOrderBy", "ListDomainsRequestOrderBy", + "ListForwardingsRequestOrderBy", "ListMailboxesRequestOrderBy", "MailboxStatus", "MAILBOX_TRANSIENT_STATUSES", @@ -63,26 +79,36 @@ "DomainRecord", "Alias", "Domain", + "Forwarding", "BatchCreateMailboxesRequest", "BatchCreateMailboxesResponse", "CreateAliasRequest", "CreateDomainRequest", + "CreateForwardingRequest", "DeleteAliasRequest", "DeleteDomainRequest", + "DeleteForwardingRequest", "DeleteMailboxRequest", "GetAliasRequest", "GetDomainRecordsRequest", "GetDomainRecordsResponse", "GetDomainRequest", + "GetForwardingRequest", + "GetMailboxForwardingRequest", "GetMailboxRequest", "ListAliasesRequest", "ListAliasesResponse", "ListDomainsRequest", "ListDomainsResponse", + "ListForwardingsRequest", + "ListForwardingsResponse", "ListMailboxesRequest", "ListMailboxesResponse", + "MailboxForwarding", "RestoreMailboxRequest", "UpdateAliasRequest", + "UpdateForwardingRequest", + "UpdateMailboxForwardingRequest", "UpdateMailboxRequest", "ValidateDomainRecordsRequest", "MailboxV1Alpha1API", diff --git a/scaleway/scaleway/mailbox/v1alpha1/api.py b/scaleway/scaleway/mailbox/v1alpha1/api.py index d8f732444..32246a050 100644 --- a/scaleway/scaleway/mailbox/v1alpha1/api.py +++ b/scaleway/scaleway/mailbox/v1alpha1/api.py @@ -5,7 +5,9 @@ from scaleway_core.api import API from scaleway_core.utils import ( + OneOfPossibility, WaitForOptions, + resolve_one_of, validate_path_param, fetch_all_pages, wait_for_resource, @@ -13,8 +15,10 @@ from .types import ( AliasStatus, DomainStatus, + ForwardingStatus, ListAliasesRequestOrderBy, ListDomainsRequestOrderBy, + ListForwardingsRequestOrderBy, ListMailboxesRequestOrderBy, MailboxStatus, MailboxSubscriptionPeriod, @@ -24,33 +28,46 @@ BatchCreateMailboxesResponse, CreateAliasRequest, CreateDomainRequest, + CreateForwardingRequest, Domain, + Forwarding, GetDomainRecordsResponse, ListAliasesResponse, ListDomainsResponse, + ListForwardingsResponse, ListMailboxesResponse, Mailbox, + MailboxForwarding, UpdateAliasRequest, + UpdateForwardingRequest, + UpdateMailboxForwardingRequest, UpdateMailboxRequest, ) from .content import ( ALIAS_TRANSIENT_STATUSES, DOMAIN_TRANSIENT_STATUSES, + FORWARDING_TRANSIENT_STATUSES, MAILBOX_TRANSIENT_STATUSES, ) from .marshalling import ( unmarshal_Mailbox, unmarshal_Alias, unmarshal_Domain, + unmarshal_Forwarding, unmarshal_BatchCreateMailboxesResponse, unmarshal_GetDomainRecordsResponse, unmarshal_ListAliasesResponse, unmarshal_ListDomainsResponse, + unmarshal_ListForwardingsResponse, unmarshal_ListMailboxesResponse, + unmarshal_MailboxForwarding, marshal_BatchCreateMailboxesRequest, marshal_CreateAliasRequest, marshal_CreateDomainRequest, + marshal_CreateForwardingRequest, marshal_UpdateAliasRequest, + marshal_UpdateForwardingRequest, + marshal_UpdateMailboxForwardingRequest, marshal_UpdateMailboxRequest, ) @@ -856,3 +873,331 @@ def delete_alias( self._throw_on_error(res) return unmarshal_Alias(res.json()) + + def create_forwarding( + self, + *, + mailbox_id: str, + email: str, + ) -> Forwarding: + """ + Create a forwarding rule for a mailbox. + All incoming emails to the mailbox will be redirected to the specified destination email address. + A mailbox can have up to 5 forwarding rules. Forwarding to the mailbox's own email address is not allowed. + :param mailbox_id: ID of the mailbox for which to create the forwarding rule. + :param email: Destination email address to which incoming emails will be forwarded. Must not be the same as the mailbox's own email address. + :return: :class:`Forwarding ` + + Usage: + :: + + result = api.create_forwarding( + mailbox_id="example", + email="example", + ) + """ + + res = self._request( + "POST", + "/mailbox/v1alpha1/forwardings", + body=marshal_CreateForwardingRequest( + CreateForwardingRequest( + mailbox_id=mailbox_id, + email=email, + ), + self.client, + ), + ) + + self._throw_on_error(res) + return unmarshal_Forwarding(res.json()) + + def list_forwardings( + self, + *, + order_by: Optional[ListForwardingsRequestOrderBy] = None, + page: Optional[int] = None, + page_size: Optional[int] = None, + mailbox_id: Optional[str] = None, + status: Optional[ForwardingStatus] = None, + project_id: Optional[str] = None, + organization_id: Optional[str] = None, + ) -> ListForwardingsResponse: + """ + List forwarding rules in an organization. + The return list can be filtered with request parameters. + :param order_by: Order forwardings by specific criteria. + :param page: Requested page number. Value must be greater or equal to 1. + :param page_size: Requested page size. Value must be between 1 and 100. + :param mailbox_id: (Optional) ID of the mailbox for which to list forwarding rules. + :param status: (Optional) Filter forwarding rules by their status. + :param project_id: ID of the Project to filter on. + One-Of ('scope'): at most one of 'project_id', 'organization_id' could be set. + :param organization_id: ID of the Organization to filter on. + One-Of ('scope'): at most one of 'project_id', 'organization_id' could be set. + :return: :class:`ListForwardingsResponse ` + + Usage: + :: + + result = api.list_forwardings() + """ + + res = self._request( + "GET", + "/mailbox/v1alpha1/forwardings", + params={ + "mailbox_id": mailbox_id, + "order_by": order_by, + "page": page, + "page_size": page_size or self.client.default_page_size, + "status": status, + **resolve_one_of( + [ + OneOfPossibility("organization_id", organization_id), + OneOfPossibility("project_id", project_id), + ] + ), + }, + ) + + self._throw_on_error(res) + return unmarshal_ListForwardingsResponse(res.json()) + + def list_forwardings_all( + self, + *, + order_by: Optional[ListForwardingsRequestOrderBy] = None, + page: Optional[int] = None, + page_size: Optional[int] = None, + mailbox_id: Optional[str] = None, + status: Optional[ForwardingStatus] = None, + project_id: Optional[str] = None, + organization_id: Optional[str] = None, + ) -> list[Forwarding]: + """ + List forwarding rules in an organization. + The return list can be filtered with request parameters. + :param order_by: Order forwardings by specific criteria. + :param page: Requested page number. Value must be greater or equal to 1. + :param page_size: Requested page size. Value must be between 1 and 100. + :param mailbox_id: (Optional) ID of the mailbox for which to list forwarding rules. + :param status: (Optional) Filter forwarding rules by their status. + :param project_id: ID of the Project to filter on. + One-Of ('scope'): at most one of 'project_id', 'organization_id' could be set. + :param organization_id: ID of the Organization to filter on. + One-Of ('scope'): at most one of 'project_id', 'organization_id' could be set. + :return: :class:`list[Forwarding] ` + + Usage: + :: + + result = api.list_forwardings_all() + """ + + return fetch_all_pages( + type=ListForwardingsResponse, + key="forwardings", + fetcher=self.list_forwardings, + args={ + "order_by": order_by, + "page": page, + "page_size": page_size, + "mailbox_id": mailbox_id, + "status": status, + "project_id": project_id, + "organization_id": organization_id, + }, + ) + + def get_forwarding( + self, + *, + forwarding_id: str, + ) -> Forwarding: + """ + Get a forwarding rule by its ID. + :param forwarding_id: ID of the forwarding rule to get. + :return: :class:`Forwarding ` + + Usage: + :: + + result = api.get_forwarding( + forwarding_id="example", + ) + """ + + param_forwarding_id = validate_path_param("forwarding_id", forwarding_id) + + res = self._request( + "GET", + f"/mailbox/v1alpha1/forwardings/{param_forwarding_id}", + ) + + self._throw_on_error(res) + return unmarshal_Forwarding(res.json()) + + def wait_for_forwarding( + self, + *, + forwarding_id: str, + options: Optional[WaitForOptions[Forwarding, bool]] = None, + ) -> Forwarding: + """ + Get a forwarding rule by its ID. + :param forwarding_id: ID of the forwarding rule to get. + :return: :class:`Forwarding ` + + Usage: + :: + + result = api.get_forwarding( + forwarding_id="example", + ) + """ + + if not options: + options = WaitForOptions() + + if not options.stop: + options.stop = lambda res: res.status not in FORWARDING_TRANSIENT_STATUSES + + return wait_for_resource( + fetcher=self.get_forwarding, + options=options, + args={ + "forwarding_id": forwarding_id, + }, + ) + + def update_forwarding( + self, + *, + forwarding_id: str, + email: Optional[str] = None, + ) -> Forwarding: + """ + Update a forwarding rule's destination email address by its ID. + :param forwarding_id: ID of the forwarding rule to update. + :param email: (Optional) New destination email address for the forwarding rule. Must not be the same as the mailbox's own email address. + :return: :class:`Forwarding ` + + Usage: + :: + + result = api.update_forwarding( + forwarding_id="example", + ) + """ + + param_forwarding_id = validate_path_param("forwarding_id", forwarding_id) + + res = self._request( + "PATCH", + f"/mailbox/v1alpha1/forwardings/{param_forwarding_id}", + body=marshal_UpdateForwardingRequest( + UpdateForwardingRequest( + forwarding_id=forwarding_id, + email=email, + ), + self.client, + ), + ) + + self._throw_on_error(res) + return unmarshal_Forwarding(res.json()) + + def delete_forwarding( + self, + *, + forwarding_id: str, + ) -> Forwarding: + """ + Delete a forwarding rule by its ID. + :param forwarding_id: ID of the forwarding rule to delete. + :return: :class:`Forwarding ` + + Usage: + :: + + result = api.delete_forwarding( + forwarding_id="example", + ) + """ + + param_forwarding_id = validate_path_param("forwarding_id", forwarding_id) + + res = self._request( + "DELETE", + f"/mailbox/v1alpha1/forwardings/{param_forwarding_id}", + ) + + self._throw_on_error(res) + return unmarshal_Forwarding(res.json()) + + def get_mailbox_forwarding( + self, + *, + mailbox_id: str, + ) -> MailboxForwarding: + """ + :param mailbox_id: ID of the mailbox to get the forwarding settings for. + :return: :class:`MailboxForwarding ` + + Usage: + :: + + result = api.get_mailbox_forwarding( + mailbox_id="example", + ) + """ + + param_mailbox_id = validate_path_param("mailbox_id", mailbox_id) + + res = self._request( + "GET", + f"/mailbox/v1alpha1/mailboxes/{param_mailbox_id}/forwarding", + ) + + self._throw_on_error(res) + return unmarshal_MailboxForwarding(res.json()) + + def update_mailbox_forwarding( + self, + *, + mailbox_id: str, + keep_copy: Optional[bool] = None, + enabled: Optional[bool] = None, + ) -> MailboxForwarding: + """ + :param mailbox_id: ID of the mailbox to update the forwarding settings for. + :param keep_copy: (Optional) Whether to keep a copy of forwarded emails in the local mailbox. + :param enabled: (Optional) Enable or disable forwarding for the mailbox. + :return: :class:`MailboxForwarding ` + + Usage: + :: + + result = api.update_mailbox_forwarding( + mailbox_id="example", + ) + """ + + param_mailbox_id = validate_path_param("mailbox_id", mailbox_id) + + res = self._request( + "PATCH", + f"/mailbox/v1alpha1/mailboxes/{param_mailbox_id}/forwarding", + body=marshal_UpdateMailboxForwardingRequest( + UpdateMailboxForwardingRequest( + mailbox_id=mailbox_id, + keep_copy=keep_copy, + enabled=enabled, + ), + self.client, + ), + ) + + self._throw_on_error(res) + return unmarshal_MailboxForwarding(res.json()) diff --git a/scaleway/scaleway/mailbox/v1alpha1/content.py b/scaleway/scaleway/mailbox/v1alpha1/content.py index ae7df61bf..a0a458b38 100644 --- a/scaleway/scaleway/mailbox/v1alpha1/content.py +++ b/scaleway/scaleway/mailbox/v1alpha1/content.py @@ -5,6 +5,7 @@ AliasStatus, DomainRecordStatus, DomainStatus, + ForwardingStatus, MailboxStatus, ) @@ -30,6 +31,14 @@ """ Lists transient statutes of the enum :class:`DomainStatus `. """ +FORWARDING_TRANSIENT_STATUSES: list[ForwardingStatus] = [ + ForwardingStatus.PROVISIONING, + ForwardingStatus.UPDATING, + ForwardingStatus.DELETING, +] +""" +Lists transient statutes of the enum :class:`ForwardingStatus `. +""" MAILBOX_TRANSIENT_STATUSES: list[MailboxStatus] = [ MailboxStatus.CREATING, MailboxStatus.WAITING_PAYMENT, diff --git a/scaleway/scaleway/mailbox/v1alpha1/marshalling.py b/scaleway/scaleway/mailbox/v1alpha1/marshalling.py index 76878a551..a81f5064a 100644 --- a/scaleway/scaleway/mailbox/v1alpha1/marshalling.py +++ b/scaleway/scaleway/mailbox/v1alpha1/marshalling.py @@ -11,22 +11,29 @@ DomainRecordLevel, DomainRecordStatus, DomainStatus, + ForwardingStatus, MailboxStatus, MailboxSubscriptionPeriod, Mailbox, Alias, Domain, + Forwarding, BatchCreateMailboxesResponse, DomainRecord, GetDomainRecordsResponse, ListAliasesResponse, ListDomainsResponse, + ListForwardingsResponse, ListMailboxesResponse, + MailboxForwarding, BatchCreateMailboxesRequestMailboxParameters, BatchCreateMailboxesRequest, CreateAliasRequest, CreateDomainRequest, + CreateForwardingRequest, UpdateAliasRequest, + UpdateForwardingRequest, + UpdateMailboxForwardingRequest, UpdateMailboxRequest, ) @@ -266,6 +273,53 @@ def unmarshal_Domain(data: Any) -> Domain: return Domain(**args) +def unmarshal_Forwarding(data: Any) -> Forwarding: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'Forwarding' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("id", None) + if field is not None: + args["id"] = field + else: + args["id"] = None + + field = data.get("mailbox_id", None) + if field is not None: + args["mailbox_id"] = field + else: + args["mailbox_id"] = None + + field = data.get("email", None) + if field is not None: + args["email"] = field + else: + args["email"] = None + + field = data.get("status", None) + if field is not None: + args["status"] = field + else: + args["status"] = ForwardingStatus.UNKNOWN_STATUS + + field = data.get("created_at", None) + if field is not None: + args["created_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["created_at"] = None + + field = data.get("updated_at", None) + if field is not None: + args["updated_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["updated_at"] = None + + return Forwarding(**args) + + def unmarshal_BatchCreateMailboxesResponse(data: Any) -> BatchCreateMailboxesResponse: if not isinstance(data, dict): raise TypeError( @@ -489,6 +543,31 @@ def unmarshal_ListDomainsResponse(data: Any) -> ListDomainsResponse: return ListDomainsResponse(**args) +def unmarshal_ListForwardingsResponse(data: Any) -> ListForwardingsResponse: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'ListForwardingsResponse' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("total_count", None) + if field is not None: + args["total_count"] = field + else: + args["total_count"] = 0 + + field = data.get("forwardings", None) + if field is not None: + args["forwardings"] = ( + [unmarshal_Forwarding(v) for v in field] if field is not None else None + ) + else: + args["forwardings"] = [] + + return ListForwardingsResponse(**args) + + def unmarshal_ListMailboxesResponse(data: Any) -> ListMailboxesResponse: if not isinstance(data, dict): raise TypeError( @@ -514,6 +593,47 @@ def unmarshal_ListMailboxesResponse(data: Any) -> ListMailboxesResponse: return ListMailboxesResponse(**args) +def unmarshal_MailboxForwarding(data: Any) -> MailboxForwarding: + if not isinstance(data, dict): + raise TypeError( + "Unmarshalling the type 'MailboxForwarding' failed as data isn't a dictionary." + ) + + args: dict[str, Any] = {} + + field = data.get("mailbox_id", None) + if field is not None: + args["mailbox_id"] = field + else: + args["mailbox_id"] = None + + field = data.get("keep_copy", None) + if field is not None: + args["keep_copy"] = field + else: + args["keep_copy"] = False + + field = data.get("enabled", None) + if field is not None: + args["enabled"] = field + else: + args["enabled"] = False + + field = data.get("created_at", None) + if field is not None: + args["created_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["created_at"] = None + + field = data.get("updated_at", None) + if field is not None: + args["updated_at"] = parser.isoparse(field) if isinstance(field, str) else field + else: + args["updated_at"] = None + + return MailboxForwarding(**args) + + def marshal_BatchCreateMailboxesRequestMailboxParameters( request: BatchCreateMailboxesRequestMailboxParameters, defaults: ProfileDefaults, @@ -585,6 +705,21 @@ def marshal_CreateDomainRequest( return output +def marshal_CreateForwardingRequest( + request: CreateForwardingRequest, + defaults: ProfileDefaults, +) -> dict[str, Any]: + output: dict[str, Any] = {} + + if request.mailbox_id is not None: + output["mailbox_id"] = request.mailbox_id + + if request.email is not None: + output["email"] = request.email + + return output + + def marshal_UpdateAliasRequest( request: UpdateAliasRequest, defaults: ProfileDefaults, @@ -597,6 +732,33 @@ def marshal_UpdateAliasRequest( return output +def marshal_UpdateForwardingRequest( + request: UpdateForwardingRequest, + defaults: ProfileDefaults, +) -> dict[str, Any]: + output: dict[str, Any] = {} + + if request.email is not None: + output["email"] = request.email + + return output + + +def marshal_UpdateMailboxForwardingRequest( + request: UpdateMailboxForwardingRequest, + defaults: ProfileDefaults, +) -> dict[str, Any]: + output: dict[str, Any] = {} + + if request.keep_copy is not None: + output["keep_copy"] = request.keep_copy + + if request.enabled is not None: + output["enabled"] = request.enabled + + return output + + def marshal_UpdateMailboxRequest( request: UpdateMailboxRequest, defaults: ProfileDefaults, diff --git a/scaleway/scaleway/mailbox/v1alpha1/types.py b/scaleway/scaleway/mailbox/v1alpha1/types.py index e06a89ba9..c3cf27e53 100644 --- a/scaleway/scaleway/mailbox/v1alpha1/types.py +++ b/scaleway/scaleway/mailbox/v1alpha1/types.py @@ -68,6 +68,17 @@ def __str__(self) -> str: return str(self.value) +class ForwardingStatus(str, Enum, metaclass=StrEnumMeta): + UNKNOWN_STATUS = "unknown_status" + PROVISIONING = "provisioning" + READY = "ready" + UPDATING = "updating" + DELETING = "deleting" + + def __str__(self) -> str: + return str(self.value) + + class ListAliasesRequestOrderBy(str, Enum, metaclass=StrEnumMeta): CREATED_AT_DESC = "created_at_desc" CREATED_AT_ASC = "created_at_asc" @@ -94,6 +105,18 @@ def __str__(self) -> str: return str(self.value) +class ListForwardingsRequestOrderBy(str, Enum, metaclass=StrEnumMeta): + CREATED_AT_DESC = "created_at_desc" + CREATED_AT_ASC = "created_at_asc" + UPDATED_AT_DESC = "updated_at_desc" + UPDATED_AT_ASC = "updated_at_asc" + EMAIL_DESC = "email_desc" + EMAIL_ASC = "email_asc" + + def __str__(self) -> str: + return str(self.value) + + class ListMailboxesRequestOrderBy(str, Enum, metaclass=StrEnumMeta): CREATED_AT_DESC = "created_at_desc" CREATED_AT_ASC = "created_at_asc" @@ -361,6 +384,39 @@ class Domain: """ +@dataclass +class Forwarding: + id: str + """ + Unique identifier of the forwarding rule. + """ + + mailbox_id: str + """ + ID of the mailbox to which the forwarding rule belongs. + """ + + email: str + """ + Destination email address to which incoming emails are forwarded. + """ + + status: ForwardingStatus + """ + Status of the forwarding rule. + """ + + created_at: Optional[datetime] = None + """ + Date and time of forwarding rule creation. + """ + + updated_at: Optional[datetime] = None + """ + Date and time when the forwarding rule was last updated. + """ + + @dataclass class BatchCreateMailboxesRequest: domain_id: str @@ -422,6 +478,19 @@ class CreateDomainRequest: """ +@dataclass +class CreateForwardingRequest: + mailbox_id: str + """ + ID of the mailbox for which to create the forwarding rule. + """ + + email: str + """ + Destination email address to which incoming emails will be forwarded. Must not be the same as the mailbox's own email address. + """ + + @dataclass class DeleteAliasRequest: alias_id: str @@ -438,6 +507,14 @@ class DeleteDomainRequest: """ +@dataclass +class DeleteForwardingRequest: + forwarding_id: str + """ + ID of the forwarding rule to delete. + """ + + @dataclass class DeleteMailboxRequest: mailbox_id: str @@ -533,6 +610,22 @@ class GetDomainRequest: """ +@dataclass +class GetForwardingRequest: + forwarding_id: str + """ + ID of the forwarding rule to get. + """ + + +@dataclass +class GetMailboxForwardingRequest: + mailbox_id: str + """ + ID of the mailbox to get the forwarding settings for. + """ + + @dataclass class GetMailboxRequest: mailbox_id: str @@ -612,6 +705,53 @@ class ListDomainsResponse: """ +@dataclass +class ListForwardingsRequest: + order_by: Optional[ListForwardingsRequestOrderBy] = ( + ListForwardingsRequestOrderBy.CREATED_AT_DESC + ) + """ + Order forwardings by specific criteria. + """ + + page: Optional[int] = 0 + """ + Requested page number. Value must be greater or equal to 1. + """ + + page_size: Optional[int] = 0 + """ + Requested page size. Value must be between 1 and 100. + """ + + mailbox_id: Optional[str] = None + """ + (Optional) ID of the mailbox for which to list forwarding rules. + """ + + status: Optional[ForwardingStatus] = ForwardingStatus.UNKNOWN_STATUS + """ + (Optional) Filter forwarding rules by their status. + """ + + project_id: Optional[str] = None + + organization_id: Optional[str] = None + + +@dataclass +class ListForwardingsResponse: + total_count: int + """ + Number of forwarding rules that match the request (without pagination). + """ + + forwardings: list[Forwarding] + """ + Single page of forwarding rules matching the requested criteria. + """ + + @dataclass class ListMailboxesRequest: order_by: Optional[ListMailboxesRequestOrderBy] = ( @@ -665,6 +805,34 @@ class ListMailboxesResponse: """ +@dataclass +class MailboxForwarding: + mailbox_id: str + """ + ID of the mailbox to which the forwarding settings belong. + """ + + keep_copy: bool + """ + Whether to keep a copy of forwarded emails in the local mailbox. + """ + + enabled: bool + """ + Whether forwarding is enabled for the mailbox. When disabled, no emails are forwarded. + """ + + created_at: Optional[datetime] = None + """ + Date and time of the forwarding settings creation. + """ + + updated_at: Optional[datetime] = None + """ + Date and time when the forwarding settings were last updated. + """ + + @dataclass class RestoreMailboxRequest: mailbox_id: str @@ -686,6 +854,37 @@ class UpdateAliasRequest: """ +@dataclass +class UpdateForwardingRequest: + forwarding_id: str + """ + ID of the forwarding rule to update. + """ + + email: Optional[str] = None + """ + (Optional) New destination email address for the forwarding rule. Must not be the same as the mailbox's own email address. + """ + + +@dataclass +class UpdateMailboxForwardingRequest: + mailbox_id: str + """ + ID of the mailbox to update the forwarding settings for. + """ + + keep_copy: Optional[bool] = False + """ + (Optional) Whether to keep a copy of forwarded emails in the local mailbox. + """ + + enabled: Optional[bool] = False + """ + (Optional) Enable or disable forwarding for the mailbox. + """ + + @dataclass class UpdateMailboxRequest: mailbox_id: str