Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion resend/domains/_domain.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import List, Union

from typing_extensions import TypedDict
from typing_extensions import NotRequired, TypedDict

from resend.domains._record import Record

Expand Down Expand Up @@ -34,3 +34,15 @@ class Domain(TypedDict):
"""
Wether the domain is deleted or not
"""
open_tracking: NotRequired[bool]
"""
Track the open rate of each email.
"""
click_tracking: NotRequired[bool]
"""
Track clicks within the body of each HTML email.
"""
tracking_subdomain: NotRequired[str]
"""
The custom subdomain used for click and open tracking links (e.g., "links").
"""
23 changes: 23 additions & 0 deletions resend/domains/_domains.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ class CreateDomainResponse(BaseResponse):
status (str): Status of the domain
region (str): The region where emails will be sent from
records (Union[List[Record], None]): The list of domain records
open_tracking (bool): Whether open tracking is enabled
click_tracking (bool): Whether click tracking is enabled
tracking_subdomain (str): The custom subdomain for tracking links
"""

id: str
Expand All @@ -97,6 +100,18 @@ class CreateDomainResponse(BaseResponse):
"""
The list of domain records
"""
open_tracking: NotRequired[bool]
"""
Track email opens
"""
click_tracking: NotRequired[bool]
"""
Track clicks within the body of HTML emails
"""
tracking_subdomain: NotRequired[str]
"""
The custom subdomain used for click and open tracking links (e.g., "links")
"""

class UpdateParams(TypedDict):
id: str
Expand All @@ -122,6 +137,10 @@ class UpdateParams(TypedDict):
communication must use TLS no matter what.
If the receiving server does not support TLS, the email will not be sent.
"""
tracking_subdomain: NotRequired[str]
"""
The custom subdomain used for click and open tracking links (e.g., "links").
"""

class CreateParams(TypedDict):
name: str
Expand All @@ -139,6 +158,10 @@ class CreateParams(TypedDict):
You can change this by setting the optional `custom_return_path` parameter
when creating a domain via the API or under Advanced options in the dashboard.
"""
tracking_subdomain: NotRequired[str]
"""
The custom subdomain used for click and open tracking links (e.g., "links").
"""

@classmethod
def create(cls, params: CreateParams) -> CreateDomainResponse:
Expand Down
6 changes: 3 additions & 3 deletions resend/domains/_record.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing_extensions import TypedDict
from typing_extensions import NotRequired, TypedDict


class Record(TypedDict):
record: str
"""
The domain record type, ie: SPF.
The domain record type, ie: SPF, DKIM, Inbound, Tracking.
"""
name: str
"""
Expand All @@ -26,7 +26,7 @@ class Record(TypedDict):
"""
The domain record value.
"""
priority: int
priority: NotRequired[int]
"""
The domain record priority.
"""
98 changes: 98 additions & 0 deletions tests/domains_async_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,89 @@ async def test_should_list_domains_async_raise_exception_when_no_content(
with pytest.raises(NoContentError):
_ = await resend.Domains.list_async()

async def test_domains_create_async_with_tracking_subdomain(self) -> None:
self.set_mock_json(
{
"id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0",
"name": "example.com",
"created_at": "2023-03-28T17:12:02.059593+00:00",
"status": "not_started",
"open_tracking": True,
"click_tracking": True,
"tracking_subdomain": "links",
"records": [
{
"record": "DKIM",
"name": "nhapbbryle57yxg3fbjytyodgbt2kyyg._domainkey",
"value": "nhapbbryle57yxg3fbjytyodgbt2kyyg.dkim.amazonses.com.",
"type": "CNAME",
"status": "not_started",
"ttl": "Auto",
},
{
"record": "Tracking",
"name": "links.example.com",
"value": "links1.resend-dns.com",
"type": "CNAME",
"ttl": "Auto",
"status": "not_started",
},
],
"region": "us-east-1",
}
)

create_params: resend.Domains.CreateParams = {
"name": "example.com",
"region": "us-east-1",
"tracking_subdomain": "links",
}
domain = await resend.Domains.create_async(params=create_params)
assert domain["id"] == "4dd369bc-aa82-4ff3-97de-514ae3000ee0"
assert domain["open_tracking"] is True
assert domain["click_tracking"] is True
assert domain["tracking_subdomain"] == "links"
tracking_record = next(
(r for r in (domain["records"] or []) if r["record"] == "Tracking"), None
)
assert tracking_record is not None
assert tracking_record["name"] == "links.example.com"
assert tracking_record["value"] == "links1.resend-dns.com"
assert tracking_record["type"] == "CNAME"

async def test_domains_get_async_with_tracking_fields(self) -> None:
self.set_mock_json(
{
"object": "domain",
"id": "d91cd9bd-1176-453e-8fc1-35364d380206",
"name": "example.com",
"status": "not_started",
"created_at": "2023-04-26T20:21:26.347412+00:00",
"region": "us-east-1",
"open_tracking": True,
"click_tracking": True,
"tracking_subdomain": "links",
"records": [
{
"record": "Tracking",
"name": "links.example.com",
"value": "links1.resend-dns.com",
"type": "CNAME",
"ttl": "Auto",
"status": "verified",
}
],
}
)

domain = await resend.Domains.get_async(
domain_id="d91cd9bd-1176-453e-8fc1-35364d380206",
)
assert domain["id"] == "d91cd9bd-1176-453e-8fc1-35364d380206"
assert domain["open_tracking"] is True
assert domain["click_tracking"] is True
assert domain["tracking_subdomain"] == "links"

async def test_domains_update_async(self) -> None:
self.set_mock_json(
{
Expand All @@ -171,6 +254,21 @@ async def test_domains_update_async(self) -> None:
assert domain["created_at"] == "2023-04-26T20:21:26.347412+00:00"
assert domain["region"] == "us-east-1"

async def test_domains_update_async_with_tracking_subdomain(self) -> None:
self.set_mock_json(
{
"object": "domain",
"id": "d91cd9bd-1176-453e-8fc1-35364d380206",
}
)

update_params: resend.Domains.UpdateParams = {
"id": "d91cd9bd-1176-453e-8fc1-35364d380206",
"tracking_subdomain": "links",
}
domain = await resend.Domains.update_async(params=update_params)
assert domain["id"] == "d91cd9bd-1176-453e-8fc1-35364d380206"

async def test_should_update_domains_async_raise_exception_when_no_content(
self,
) -> None:
Expand Down
100 changes: 100 additions & 0 deletions tests/domains_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,91 @@ def test_should_verify_domains_raise_exception_when_no_content(self) -> None:
domain_id="d91cd9bd-1176-453e-8fc1-35364d380206",
)

def test_domains_create_with_tracking_subdomain(self) -> None:
self.set_mock_json(
{
"id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0",
"name": "example.com",
"created_at": "2023-03-28T17:12:02.059593+00:00",
"status": "not_started",
"open_tracking": True,
"click_tracking": True,
"tracking_subdomain": "links",
"records": [
{
"record": "DKIM",
"name": "nhapbbryle57yxg3fbjytyodgbt2kyyg._domainkey",
"value": "nhapbbryle57yxg3fbjytyodgbt2kyyg.dkim.amazonses.com.",
"type": "CNAME",
"status": "not_started",
"ttl": "Auto",
},
{
"record": "Tracking",
"name": "links.example.com",
"value": "links1.resend-dns.com",
"type": "CNAME",
"ttl": "Auto",
"status": "not_started",
},
],
"region": "us-east-1",
}
)

create_params: resend.Domains.CreateParams = {
"name": "example.com",
"region": "us-east-1",
"tracking_subdomain": "links",
}
domain: resend.Domains.CreateDomainResponse = resend.Domains.create(
params=create_params
)
assert domain["id"] == "4dd369bc-aa82-4ff3-97de-514ae3000ee0"
assert domain["open_tracking"] is True
assert domain["click_tracking"] is True
assert domain["tracking_subdomain"] == "links"
tracking_record = next(
(r for r in (domain["records"] or []) if r["record"] == "Tracking"), None
)
assert tracking_record is not None
assert tracking_record["name"] == "links.example.com"
assert tracking_record["value"] == "links1.resend-dns.com"
assert tracking_record["type"] == "CNAME"

def test_domains_get_with_tracking_fields(self) -> None:
self.set_mock_json(
{
"object": "domain",
"id": "d91cd9bd-1176-453e-8fc1-35364d380206",
"name": "example.com",
"status": "not_started",
"created_at": "2023-04-26T20:21:26.347412+00:00",
"region": "us-east-1",
"open_tracking": True,
"click_tracking": True,
"tracking_subdomain": "links",
"records": [
{
"record": "Tracking",
"name": "links.example.com",
"value": "links1.resend-dns.com",
"type": "CNAME",
"ttl": "Auto",
"status": "verified",
}
],
}
)

domain = resend.Domains.get(
domain_id="d91cd9bd-1176-453e-8fc1-35364d380206",
)
assert domain["id"] == "d91cd9bd-1176-453e-8fc1-35364d380206"
assert domain["open_tracking"] is True
assert domain["click_tracking"] is True
assert domain["tracking_subdomain"] == "links"

def test_domains_update(self) -> None:
self.set_mock_json(
{
Expand All @@ -197,6 +282,21 @@ def test_domains_update(self) -> None:
domain = resend.Domains.update(params)
assert domain["id"] == "479e3145-dd38-476b-932c-529ceb705947"

def test_domains_update_with_tracking_subdomain(self) -> None:
self.set_mock_json(
{
"object": "domain",
"id": "479e3145-dd38-476b-932c-529ceb705947",
}
)

params: resend.Domains.UpdateParams = {
"id": "479e3145-dd38-476b-932c-529ceb705947",
"tracking_subdomain": "links",
}
domain = resend.Domains.update(params)
assert domain["id"] == "479e3145-dd38-476b-932c-529ceb705947"

def test_should_update_domains_raise_exception_when_no_content(self) -> None:
self.set_mock_json(None)
params: resend.Domains.UpdateParams = {
Expand Down
Loading