From c33f6bdcf7589b28405c57feacfcb28e8da6502c Mon Sep 17 00:00:00 2001 From: Daan Wagenaar Date: Wed, 22 Mar 2023 13:35:18 +0100 Subject: [PATCH 1/4] Added mock calls for the mail forward endpoints --- tests/fixtures/domains.json | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/fixtures/domains.json b/tests/fixtures/domains.json index f138e76..2753495 100644 --- a/tests/fixtures/domains.json +++ b/tests/fixtures/domains.json @@ -353,5 +353,63 @@ } ] } + }, + { + "method": "GET", + "url": "https://api.transip.nl/v6/email/example.com/mail-forwards", + "json": { + "forwards": [ + { + "id": 123, + "localPart": "forward_mailbox", + "domain": "example.com", + "status": "ready", + "forwardTo": "mailbox@otherdomain.com" + } + ] + }, + "status": 200, + "content_type": "application/json" + }, + { + "method": "GET", + "url": "https://api.transip.nl/v6/email/example.com/mail-forwards/123", + "json": { + "forward": { + "id": 123, + "localPart": "forward_mailbox", + "domain": "example.com", + "status": "ready", + "forwardTo": "mailbox@otherdomain.com" + } + }, + "status": 200, + "content_type": "application/json" + }, + { + "method": "POST", + "url": "https://api.transip.nl/v6/email/example.com/mail-forwards", + "status": 201, + "content_type": "application/json", + "match_json_params": { + "localPart": "different_local_part", + "forwardTo": "forward_to@differentdomain.com" + } + }, + { + "method": "PUT", + "url": "https://api.transip.nl/v6/email/example.com/mail-forwards/123", + "status": 204, + "content_type": "application/json", + "match_json_params": { + "localPart": "different_local_part", + "forwardTo": "forward_to@differentdomain.com" + } + }, + { + "method": "DELETE", + "url": "https://api.transip.nl/v6/email/example.com/mail-forwards/123", + "status": 204, + "content_type": "application/json" } ] From aca1976e676ec12cb5bed19836cf783e6d96903f Mon Sep 17 00:00:00 2001 From: Daan Wagenaar Date: Wed, 22 Mar 2023 13:36:16 +0100 Subject: [PATCH 2/4] Added the MailForward class and MailForwardService. Scaled these under domain as all mail forward related operations require a domain --- transip/v6/objects.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/transip/v6/objects.py b/transip/v6/objects.py index 467de04..eb7ee65 100644 --- a/transip/v6/objects.py +++ b/transip/v6/objects.py @@ -331,6 +331,32 @@ class NameserverService(ListMixin, ReplaceMixin, ApiService): ) +class MailForward(ObjectUpdateMixin, ObjectDeleteMixin, ApiObject): + + _id_attr: Optional[str] = "id" + + +class MailForwardService(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, + ListMixin, ApiService): + """Service to manage mail forwards for a domain.""" + + _path: str = "/email/{parent_id}/mail-forwards" + _obj_cls: Optional[Type[ApiObject]] = MailForward + + _resp_list_attr: str = "forwards" + _resp_get_attr: str = "forward" + + _create_attrs: Optional[AttrsTuple] = ( + ("localPart", "forwardTo",), # required + tuple() # optional + ) + + _update_attrs: Optional[AttrsTuple] = ( + ("localPart", "forwardTo",), # required + tuple() # optional + ) + + class Domain(ApiObject): _id_attr: str = "name" @@ -359,6 +385,14 @@ def nameservers(self) -> NameserverService: parent=self # type: ignore ) + @property + def mail_forwards(self) -> MailForwardService: + """Return the service to manage mail forwards for a domain.""" + return MailForwardService( + self.service.client, + parent=self # type: ignore + ) + class DomainService(CreateMixin, GetMixin, DeleteMixin, ListMixin, ApiService): """Service to manage domains.""" From ac1608fced8a623141bc4bd0f23674ff5b443eae Mon Sep 17 00:00:00 2001 From: Daan Wagenaar Date: Wed, 22 Mar 2023 13:36:22 +0100 Subject: [PATCH 3/4] Added readme for mail forwards --- README.md | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/README.md b/README.md index 750c8d8..89f27e5 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,13 @@ - [The **Nameserver** class](#the-nameserver-class) - [List nameservers for a domain](#list-nameservers-for-a-domain) - [Update nameservers for a domain](#update-nameservers-for-a-domain) + - [Mail Forwards](#mail-forwards) + - [The **MailForward** class](#the-mailforward-class) + - [List all mail forwards for a domain](#list-all-mail-forwards) + - [Get mail forward by id](#get-mail-forward-by-id) + - [Add a new mail forward](#add-a-new-mail-forward) + - [Update a mail forward](#update-a-mail-forward) + - [Delete a mail forward](#delete-a-mail-forward) - [VPS](#vps) - [HA-IP](#ha-ip) - [Colocation](#colocation) @@ -704,6 +711,124 @@ nameserver[0].ipv4 = '195.135.195.195' domain.nameservers.replace(nameservers) ``` +### Mail Forwards +### The **MailForward** class +When listing all mail forwards for a domain, a list of **transip.v6.objects.MailForward** objects is returned. + +**_class_ MailForward** + +The **MailForward** class makes the following attributes available: + +- **id**: The mail forward identifier. +- **localPart**: The local part of the mailbox for the forward. +- **domain**: The domain to which the forward applies. +- **status**: Status of the forward +- **forwardTo**: The email address to forward to. + +The class has the following methods: + +- **delete()** will delete the mail forward +- **update()** will send the updated attributes to the TransIP API. + +#### List all mail forwards +Retrieve all mail forwards for a domain by calling **mail_forwards.list()** on a **transip.v6.objects.Domain** object. +This will return a list of **transip.v6.objects.MailForward** objects. + +For example: +```python +import transip +# Initialize a client using the TransIP demo token. +client = transip.TransIP(access_token=transip.v6.DEMO_TOKEN) + +# List all mail forwards for a domain. +domain = client.domains.get("transipdemonstratie.nl") +mail_forwards = domain.mail_forwards.list() + +# Show mail forwards +for mail_forward in mail_forwards: + print(f"id: {mail_forward.id}, Domain: {mail_forward.domain}, forwardTo: {mail_forward.forwardTo}, " + f"localPart: {mail_forward.localPart}, status: {mail_forward.status}") +``` + +#### Get mail forward by id +Retrieve a single mail forward by its ID by calling **mail_forwards.get()** on a **transip.v6.objects.Domain** object. +This will return a **transip.v6.objects.MailForward** object. + +For example: +```python +import transip +# Initialize a client using the TransIP demo token. +client = transip.TransIP(access_token=transip.v6.DEMO_TOKEN) +domain = client.domains.get("transipdemonstratie.nl") + +# Retrieve a mail forward by its ID. +mail_forward = domain.mail_forwards.get(123) + +print(f"id: {mail_forward.id}, Domain: {mail_forward.domain}, forwardTo: {mail_forward.forwardTo}, " + f"localPart: {mail_forward.localPart}, status: {mail_forward.status}") +``` + +#### Add a new mail forward +Add a new mail forward by calling **mail_forwards.create(_data_)** on a **transip.v6.objects.Domain** object. +The **data** keyword argument requires a dictionary with the **localPart** and **forwardTo** attributes. + +**Note:** Providing an empty string as the `localPart` creates a catch-all address + +For example: +```python +import transip +# Initialize a client using the TransIP demo token. +client = transip.TransIP(access_token=transip.v6.DEMO_TOKEN) + +# Data used to create a new mail forward. +forward_data = { + "localPart": "forward", + "forwardTo": "mailbox@domain.com" +} + +domain = client.domains.get("transipdemonstratie.nl") +domain.mail_forwards.create(forward_data) +``` + +#### Update a mail forward +Update an existing mail forward by calling **mail_forwards.update(_id_, _data_)** on a **transip.v6.objects.Domain** object. + +For example: +```python +import transip +# Initialize a client using the TransIP demo token. +client = transip.TransIP(access_token=transip.v6.DEMO_TOKEN) + +# Data used to create a new mail forward. +forward_data = { + "localPart": "forward", + "forwardTo": "mailbox@domain.com" +} + +# Update mail forward with id 123. +domain = client.domains.get("transipdemonstratie.nl") +domain.mail_forwards.update(123, forward_data) +``` + +The **transip.v6.objects.MailForward** class also provides an **update()** method to update a **MailForward** object +from an instance after changing any of the update-able attributes. + +#### Delete a mail forward +Delete an existing mail forward by calling **mail_forwards.delete(_id_)** on a **transip.v6.objects.Domain** object. +The **id** keyword argument is the ID of the mail forward provided by TransIP. + +For example: +```python +import transip +# Initialize a client using the TransIP demo token. +client = transip.TransIP(access_token=transip.v6.DEMO_TOKEN) + +domain = client.domains.get("transipdemonstratie.nl") +domain.mail_forwards.delete(224063) +``` + +The **transip.v6.objects.MailForward** class also provides a **delete()** method to delete a **MailForward** object from an instance. + ## VPS The documentation for managing **VPSs** and related resources has not yet been documented. Feel free to file an [issue](https://github.com/roaldnefs/python-transip/issues/new/choose) for adding the missing section(s) in the documentation. From 7954f358d613ba6bacd1c4a72b516b83227f1682 Mon Sep 17 00:00:00 2001 From: Daan Wagenaar Date: Wed, 22 Mar 2023 13:36:31 +0100 Subject: [PATCH 4/4] Added tests for the mail forwards --- tests/services/test_domains.py | 83 +++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/tests/services/test_domains.py b/tests/services/test_domains.py index 7a0184d..d5108f3 100644 --- a/tests/services/test_domains.py +++ b/tests/services/test_domains.py @@ -23,7 +23,7 @@ from typing import List, Dict, Any, Union from transip import TransIP -from transip.v6.objects import Domain, WhoisContact, Nameserver, DnsEntry +from transip.v6.objects import Domain, WhoisContact, Nameserver, DnsEntry, MailForward, MailForwardService from tests.utils import load_responses_fixtures @@ -120,7 +120,6 @@ def test_dns_replace(self) -> None: except Exception as exc: assert False, f"'transip.v6.objects.Domain.dns.replace' raised an exception {exc}" - @responses.activate def test_dns_create(self) -> None: dns_entry_data: Dict[str, Union[str, int]] = { @@ -215,3 +214,83 @@ def test_dns_delete_object(self) -> None: # the listing of the DNS entries and the deletion of a single DNS # entry. self.assertEqual(len(responses.calls), 3) + + @responses.activate + def test_mail_forwards_list(self) -> None: + """ + Check if the mail forwards of a single domain can be listed. + """ + domain: Domain = self.client.domains.get("example.com") # type: ignore + mail_forwards: List[MailForward] = domain.mail_forwards.list() # type: ignore + self.assertEqual(len(mail_forwards), 1) + + forward: MailForward = mail_forwards[0] + + self.assertEqual(forward.get_id(), 123) # type: ignore + + @responses.activate + def test_mail_forwards_get(self) -> None: + forward_id: int = 123 + + domain: Domain = self.client.domains.get("example.com") # type: ignore + forward: MailForward = domain.mail_forwards.get(forward_id) # type: ignore + + self.assertEqual(forward.get_id(), forward_id) # type: ignore + + @responses.activate + def test_delete(self) -> None: + forward_id: int = 123 + domain: Domain = self.client.domains.get("example.com") # type: ignore + + try: + domain.mail_forwards.delete(forward_id) # type: ignore + except Exception as exc: + assert False, f"'transip.TransIP.mail_forwards.delete' raised an exception {exc}" + + @responses.activate + def test_delete_object(self) -> None: + forward_id: int = 123 + forward: MailForward = self.client.domains.get("example.com").mail_forwards.get(forward_id) # type: ignore + + try: + forward.delete() # type: ignore + except Exception as exc: + assert False, f"'transip.v6.objects.MailForward.delete' raised an exception {exc}" + + @responses.activate + def test_update_object(self) -> None: + forward_id: int = 123 + forward: MailForward = self.client.domains.get("example.com").mail_forwards.get(forward_id) # type: ignore + forward.localPart = "different_local_part" + forward.forwardTo = "forward_to@differentdomain.com" + + try: + forward.update() + except Exception as exc: + assert False, f"'transip.v6.objects.MailForward.update' raised an exception {exc}" + + @responses.activate + def test_update(self) -> None: + forward_id: int = 123 + mail_forward_data: Dict[str, str] = { + "localPart": "different_local_part", + "forwardTo": "forward_to@differentdomain.com" + } + + try: + self.client.domains.get("example.com").mail_forwards.update( # type: ignore + forward_id, mail_forward_data + ) + except Exception as exc: + assert False, f"'transip.TransIP.mail_forwards.update' raised an exception {exc}" + + @responses.activate + def test_create(self) -> None: + mail_forward_data: Dict[str, str] = { + "localPart": "different_local_part", + "forwardTo": "forward_to@differentdomain.com" + } + self.client.domains.get("example.com").mail_forwards.create(mail_forward_data) # type: ignore + + assert len(responses.calls) == 2 + assert responses.calls[1].response.status_code == 201