diff --git a/examples/domains.py b/examples/domains.py index 5fd0374..b68940b 100644 --- a/examples/domains.py +++ b/examples/domains.py @@ -6,30 +6,38 @@ raise EnvironmentError("RESEND_API_KEY is missing") -domain = resend.Domains.create( - { - "name": "domain.io", - } -) -print(domain) - -retrieved = resend.Domains.get(domain_id=domain["id"]) -print(retrieved) - -update_params = { - "id": domain["id"], +domain = resend.Domains.create({ + "name": "example.com", + "region": "us-east-1", +}) +print(f'Crated domain {domain.name} with id {domain.id}') + +retrieved = resend.Domains.get(domain_id=domain.id) +print(retrieved.__dict__) +for record in retrieved.records: + print(record.__dict__) + +update_params: resend.Domains.UpdateDomainRequestParams = { + "id": domain.id, "open_tracking": True, "click_tracking": True, } -updated = resend.Domains.update(update_params) -print(updated) +updated_domain = resend.Domains.update(update_params) +print(f'Updated domain: {updated_domain.id}') domains = resend.Domains.list() -print(domains) +if not domains: + print("No domains found") +for domain in domains: + print(domain.__dict__) -resend.Domains.verify(domain_id=domain["id"]) +resend.Domains.verify(domain_id=domain.id) print("domain verified") -resend.Domains.remove(domain_id=domain["id"]) -print("domain removed") +domain = resend.Domains.remove(domain_id=domain.id) +print(f"domain id: {domain.id} deleted: {domain.deleted}") + +domain = resend.Domains.verify(domain_id=domain.id) +print(f'Verified domain: {domain.id}') +print(domain.id) \ No newline at end of file diff --git a/resend/__init__.py b/resend/__init__.py index dd90fc0..dd02e9d 100644 --- a/resend/__init__.py +++ b/resend/__init__.py @@ -4,7 +4,7 @@ from .audiences import Audiences from .batch import Batch from .contacts import Contacts -from .domains import Domains +from .domains._domains import Domains from .emails import Emails from .request import Request from .version import get_version diff --git a/resend/api_keys/_api_keys.py b/resend/api_keys/_api_keys.py index 758c617..5d9b887 100644 --- a/resend/api_keys/_api_keys.py +++ b/resend/api_keys/_api_keys.py @@ -7,9 +7,6 @@ class ApiKeys: - """ - Api Keys module - """ class CreateApiKeyRequestParams(TypedDict): name: str diff --git a/resend/domains/__init__.py b/resend/domains/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resend/domains/_domain.py b/resend/domains/_domain.py new file mode 100644 index 0000000..0d54912 --- /dev/null +++ b/resend/domains/_domain.py @@ -0,0 +1,57 @@ +from typing import List +from resend.domains._record import Record + + +class Domain: + id: str + """ + The domain ID + """ + name: str + """ + The domain name + """ + created_at: str + """ + When domain was created + """ + status: str + """ + Status of the domain: not_started, etc.. + """ + region: str + """ + The region where emails will be sent from. Possible values: us-east-1' | 'eu-west-1' | 'sa-east-1' | 'ap-northeast-1' + """ + records: List[Record] + """ + The list of domain records + """ + deleted: bool + """ + Wether the domain is deleted or not + """ + + def __init__( + self, id, name, region, created_at, + status, records, deleted=False): + self.id = id + self.name = name + self.created_at = created_at + self.status = status + self.region = region + self.records = records + self.deleted = deleted + + @staticmethod + def new_from_request(val) -> "Domain": + domain = Domain( + id=val["id"] if "id" in val else None, + name=val["name"] if "name" in val else None, + region=val["region"] if "region" in val else None, + created_at=val["created_at"] if "created_at" in val else None, + status=val["status"] if "status" in val else None, + records = [Record.new_from_request(record) for record in val["records"]] if "records" in val else None, + deleted=val["deleted"] if "deleted" in val else None, + ) + return domain diff --git a/resend/domains/_domains.py b/resend/domains/_domains.py new file mode 100644 index 0000000..7757585 --- /dev/null +++ b/resend/domains/_domains.py @@ -0,0 +1,99 @@ +from typing import List, Any, Dict, Optional, cast + +from typing_extensions import TypedDict +from resend import request +from resend.domains._domain import Domain + +class Domains: + + class UpdateDomainRequestParams(TypedDict): + id: str + """ + The domain ID. + """ + click_tracking: bool + """ + Track clicks within the body of each HTML email. + """ + open_tracking: bool + """ + Track the open rate of each email. + """ + + class CreateDomainRequestParams(TypedDict): + name: str + """ + The domain name. + """ + region: str + """ + The region where emails will be sent from. + Possible values: us-east-1' | 'eu-west-1' | 'sa-east-1' | 'ap-northeast-1' + """ + + @classmethod + def create(cls, params: CreateDomainRequestParams = {}) -> Domain: + """ + Create a domain through the Resend Email API. + see more: https://resend.com/docs/api-reference/domains/create-domain + """ + path = "/domains" + return Domain.new_from_request( + request.Request( + path=path, params=cast(Dict[Any, Any], params), verb="post" + ).perform() + ) + + @classmethod + def update(cls, params: UpdateDomainRequestParams = {}) -> Domain: + """ + Update an existing domain. + see more: https://resend.com/docs/api-reference/domains/update-domain + """ + path = f"/domains/{params['id']}" + return Domain.new_from_request( + request.Request(path=path, params=params, verb="patch").perform() + ) + + @classmethod + def get(cls, domain_id: str = "") -> Domain: + """ + Retrieve a single domain for the authenticated user. + see more: https://resend.com/docs/api-reference/domains/get-domain + """ + path = f"/domains/{domain_id}" + return Domain.new_from_request( + request.Request(path=path, params={}, verb="get").perform() + ) + + @classmethod + def list(cls) -> List[Domain]: + """ + Retrieve a list of domains for the authenticated user. + see more: https://resend.com/docs/api-reference/domains/list-domains + """ + path = "/domains" + resp = request.Request(path=path, params={}, verb="get").perform() + return [Domain.new_from_request(val) for val in resp['data']] + + @classmethod + def remove(cls, domain_id: str = "") -> Domain: + """ + Remove an existing domain. + see more: https://resend.com/docs/api-reference/domains/remove-domain + """ + path = f"/domains/{domain_id}" + return Domain.new_from_request( + request.Request(path=path, params={}, verb="delete").perform() + ) + + @classmethod + def verify(cls, domain_id: str = "" ) -> Domain: + """ + Verify an existing domain. + see more: https://resend.com/docs/api-reference/domains/verify-domain + """ + path = f"/domains/{domain_id}/verify" + return Domain.new_from_request( + request.Request(path=path, params={}, verb="post").perform() + ) diff --git a/resend/domains/_record.py b/resend/domains/_record.py new file mode 100644 index 0000000..216f12f --- /dev/null +++ b/resend/domains/_record.py @@ -0,0 +1,52 @@ +class Record: + record: str + """ + The domain record type, ie: SPF. + """ + name: str + """ + The domain record name. + """ + type: str + """ + The domain record type, ie: MX. + """ + ttl: str + """ + The domain record time to live. + """ + status: str + """ + The domain record status: not_started, etc.. + """ + value: str + """ + The domain record value. + """ + priority: int + """ + The domain record priority. + """ + + def __init__( + self, record, name, type, ttl, + status, value, priority): + self.record = record + self.name = name + self.type = type + self.ttl = ttl + self.status = status + self.value = value + self.priority = priority + + @staticmethod + def new_from_request(val) -> "Record": + return Record( + record=val["record"] if "record" in val else None, + name=val["name"] if "name" in val else None, + type=val["type"] if "type" in val else None, + ttl=val["ttl"] if "ttl" in val else None, + status=val["status"] if "status" in val else None, + value=val["value"] if "value" in val else None, + priority=val["priority"] if "priority" in val else None, + ) \ No newline at end of file diff --git a/tests/domains_test.py b/tests/domains_test.py index 724cd78..ae21fd6 100644 --- a/tests/domains_test.py +++ b/tests/domains_test.py @@ -20,7 +20,7 @@ def mock_json(): return { "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0", "name": "example.com", - "createdAt": "2023-03-28T17:12:02.059593+00:00", + "created_at": "2023-03-28T17:12:02.059593+00:00", "status": "not_started", "records": [ { @@ -66,19 +66,21 @@ def mock_json(): }, ], "region": "us-east-1", - "dnsProvider": "Unidentified", } m.json = mock_json mock.return_value = m - params = { + params: resend.Domains.UpdateDomainRequestParams = { "name": "example.com", } - key = resend.Domains.create(params) - assert key["id"] == "4dd369bc-aa82-4ff3-97de-514ae3000ee0" - assert key["name"] == "example.com" - assert key["status"] == "not_started" + domain = resend.Domains.create(params) + assert domain.id == "4dd369bc-aa82-4ff3-97de-514ae3000ee0" + assert domain.name == "example.com" + assert domain.status == "not_started" + assert domain.created_at == "2023-03-28T17:12:02.059593+00:00" + assert domain.region == "us-east-1" + patcher.stop() def test_domains_get(self): @@ -103,15 +105,14 @@ def mock_json(): m.json = mock_json mock.return_value = m - email = resend.Domains.get( + domain = resend.Domains.get( domain_id="d91cd9bd-1176-453e-8fc1-35364d380206", ) - assert email["id"] == "d91cd9bd-1176-453e-8fc1-35364d380206" - assert email["object"] == "domain" - assert email["name"] == "example.com" - assert email["status"] == "not_started" - assert email["created_at"] == "2023-04-26T20:21:26.347412+00:00" - assert email["region"] == "us-east-1" + assert domain.id == "d91cd9bd-1176-453e-8fc1-35364d380206" + assert domain.name == "example.com" + assert domain.status == "not_started" + assert domain.created_at == "2023-04-26T20:21:26.347412+00:00" + assert domain.region == "us-east-1" patcher.stop() def test_domains_list(self): @@ -139,8 +140,12 @@ def mock_json(): m.json = mock_json mock.return_value = m - keys = resend.Domains.list() - assert keys["data"][0]["id"] == "d91cd9bd-1176-453e-8fc1-35364d380206" + domains = resend.Domains.list() + assert domains[0].id == "d91cd9bd-1176-453e-8fc1-35364d380206" + assert domains[0].name == "example.com" + assert domains[0].status == "not_started" + assert domains[0].created_at == "2023-04-26T20:21:26.347412+00:00" + assert domains[0].region == "us-east-1" patcher.stop() def test_domains_remove(self): @@ -152,13 +157,21 @@ def test_domains_remove(self): m = MagicMock() m.status_code = 200 - m.text = "" + def mock_json(): + return { + "object": "domain", + "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c", + "deleted": True + } + + m.json = mock_json mock.return_value = m - email = resend.Domains.remove( + domain = resend.Domains.remove( domain_id="4ef9a417-02e9-4d39-ad75-9611e0fcc33c", ) - assert email is None + assert domain.deleted is True + assert domain.id == "4ef9a417-02e9-4d39-ad75-9611e0fcc33c" patcher.stop() def test_domains_verify(self): @@ -170,13 +183,20 @@ def test_domains_verify(self): m = MagicMock() m.status_code = 200 - m.text = "" + def mock_json(): + return { + "object": "domain", + "id": "d91cd9bd-1176-453e-8fc1-35364d380206" + } + + m.json = mock_json mock.return_value = m - email = resend.Domains.verify( - domain_id="4ef9a417-02e9-4d39-ad75-9611e0fcc33c", + domain = resend.Domains.verify( + domain_id="d91cd9bd-1176-453e-8fc1-35364d380206", ) - assert email is None + assert domain.id == "d91cd9bd-1176-453e-8fc1-35364d380206" + patcher.stop() def test_domains_update(self): @@ -197,13 +217,12 @@ def mock_json(): m.json = mock_json mock.return_value = m - params = { + params: resend.Domains.UpdateDomainRequestParams = { "id": "479e3145-dd38-476b-932c-529ceb705947", "open_tracking": True, "click_tracking": True, } - contact = resend.Domains.update(params) - assert contact["id"] == "479e3145-dd38-476b-932c-529ceb705947" - assert contact["object"] == "domain" + domain = resend.Domains.update(params) + assert domain.id == "479e3145-dd38-476b-932c-529ceb705947" patcher.stop()