Skip to content
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ The same situation applies to both `client.batch_send()` and `client.sending_api
- Contact Imports – [`contacts/contact_imports.py`](examples/contacts/contact_imports.py)

### Email Templates API:
- Templates (experimental, `/api/templates`) – [`paginated_templates/templates.py`](examples/paginated_templates/templates.py)
- Templates management – [`email_templates/templates.py`](examples/email_templates/templates.py)

### Sending Domains API:
Expand Down
80 changes: 80 additions & 0 deletions examples/paginated_templates/templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import os
from typing import Optional

import mailtrap as mt
from mailtrap.models.common import DeletedObject
from mailtrap.models.paginated_templates import Template
from mailtrap.models.paginated_templates import TemplateListResponse

API_KEY = os.environ["MAILTRAP_API_KEY"]
ACCOUNT_ID = os.environ["MAILTRAP_ACCOUNT_ID"]

client = mt.MailtrapClient(token=API_KEY, account_id=ACCOUNT_ID)
templates_api = client.templates_api.templates


def list_templates() -> TemplateListResponse:
# `token` is the page number (page-token pagination); `per_page` caps at 100.
response = templates_api.get_list(mt.TemplateListParams(per_page=50, token=1))
print(response.data)
print(response.pagination)
return response


def create_template(
name: str,
subject: str,
category: str,
body_html: Optional[str] = None,
body_text: Optional[str] = None,
) -> Template:
params = mt.CreateTemplateParams(
name=name,
subject=subject,
category=category,
body_html=body_html,
body_text=body_text,
)
return templates_api.create(params)


def get_template(template_id: int) -> Template:
return templates_api.get_by_id(template_id)


def update_template(
template_id: int,
name: Optional[str] = None,
subject: Optional[str] = None,
category: Optional[str] = None,
body_html: Optional[str] = None,
body_text: Optional[str] = None,
) -> Template:
params = mt.UpdateTemplateParams(
name=name,
subject=subject,
category=category,
body_html=body_html,
body_text=body_text,
)
return templates_api.update(template_id, params)


def delete_template(template_id: int) -> DeletedObject:
return templates_api.delete(template_id)


if __name__ == "__main__":
list_templates()

created = create_template(
name="Welcome",
subject="Welcome aboard",
category="Onboarding",
body_html="<h1>Hello!</h1>",
)
print(created)

print(get_template(created.id))
print(update_template(created.id, subject="Welcome to Mailtrap"))
print(delete_template(created.id))
5 changes: 5 additions & 0 deletions mailtrap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@
from .models.mail import MailFromTemplate
from .models.messages import UpdateEmailMessageParams
from .models.organizations import CreateSubAccountParams
from .models.paginated_templates import CreateTemplateParams
from .models.paginated_templates import Template
from .models.paginated_templates import TemplateListParams
from .models.paginated_templates import TemplateListResponse
from .models.paginated_templates import UpdateTemplateParams
from .models.permissions import PermissionResourceParams
from .models.projects import ProjectParams
from .models.sending_domains import CreateSendingDomainParams
Expand Down
12 changes: 12 additions & 0 deletions mailtrap/api/paginated_templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from mailtrap.api.resources.paginated_templates import PaginatedTemplatesApi
from mailtrap.http import HttpClient


class TemplatesBaseApi:
Comment thread
izikaj marked this conversation as resolved.
def __init__(self, client: HttpClient, account_id: str) -> None:
self._account_id = account_id
self._client = client

@property
def templates(self) -> PaginatedTemplatesApi:
return PaginatedTemplatesApi(account_id=self._account_id, client=self._client)
62 changes: 62 additions & 0 deletions mailtrap/api/resources/paginated_templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from typing import Optional

from mailtrap.http import HttpClient
from mailtrap.models.common import DeletedObject
from mailtrap.models.paginated_templates import CreateTemplateParams
from mailtrap.models.paginated_templates import Template
from mailtrap.models.paginated_templates import TemplateListParams
from mailtrap.models.paginated_templates import TemplateListResponse
from mailtrap.models.paginated_templates import TemplateResponse
from mailtrap.models.paginated_templates import UpdateTemplateParams


class PaginatedTemplatesApi:
"""
Templates API. The ``/api/templates`` endpoints are experimental: their
request and response shapes may change before general availability.
"""

def __init__(self, client: HttpClient, account_id: str) -> None:
self._account_id = account_id
self._client = client

def get_list(
self, params: Optional[TemplateListParams] = None
) -> TemplateListResponse:
"""
List email templates in the account, one page at a time. Unlike the
``email_templates_api`` list, it does not return every template: pass
``pagination.next_token`` with the same ``per_page`` to get the next
page. Omit ``params`` for the first page with API defaults.
"""
query_params = params.api_query_params if params else None
response = self._client.get(self._api_path(), params=query_params or None)
return TemplateListResponse(**response)

def get_by_id(self, template_id: int) -> Template:
"""Get an email template by ID."""
response = self._client.get(self._api_path(template_id))
return TemplateResponse(**response).data

def create(self, template_params: CreateTemplateParams) -> Template:
"""Create a new email template."""
response = self._client.post(self._api_path(), json=template_params.api_data)
return TemplateResponse(**response).data

def update(self, template_id: int, template_params: UpdateTemplateParams) -> Template:
"""Update an email template. Only the supplied fields are changed."""
response = self._client.patch(
self._api_path(template_id), json=template_params.api_data
)
return TemplateResponse(**response).data

def delete(self, template_id: int) -> DeletedObject:
"""Delete an email template."""
self._client.delete(self._api_path(template_id))
return DeletedObject(template_id)

def _api_path(self, template_id: Optional[int] = None) -> str:
path = f"/api/accounts/{self._account_id}/templates"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if template_id is not None:
return f"{path}/{template_id}"
return path
9 changes: 9 additions & 0 deletions mailtrap/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from mailtrap.api.general import GeneralApi
from mailtrap.api.inbound import InboundBaseApi
from mailtrap.api.organizations import OrganizationsBaseApi
from mailtrap.api.paginated_templates import TemplatesBaseApi
from mailtrap.api.resources.stats import StatsApi
from mailtrap.api.sending import SendingApi
from mailtrap.api.sending_domains import SendingDomainsBaseApi
Expand Down Expand Up @@ -103,6 +104,14 @@ def email_templates_api(self) -> EmailTemplatesApi:
client=HttpClient(host=GENERAL_HOST, headers=self.headers),
)

@property
def templates_api(self) -> TemplatesBaseApi:
self._validate_account_id("Templates API")
return TemplatesBaseApi(
account_id=cast(str, self.account_id),
client=HttpClient(host=GENERAL_HOST, headers=self.headers),
)

@property
def contacts_api(self) -> ContactsBaseApi:
self._validate_account_id("Contacts API")
Expand Down
88 changes: 88 additions & 0 deletions mailtrap/models/paginated_templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""Models for the account-scoped, paginated Templates API (``/api/templates``)."""

from typing import Optional

from pydantic import Field
from pydantic.dataclasses import dataclass

from mailtrap.models.common import Pagination
from mailtrap.models.common import RequestParams


@dataclass
class Template:
"""A single email template."""

id: int
uuid: Optional[str] = None
name: Optional[str] = None
category: Optional[str] = None
subject: Optional[str] = None
body_html: Optional[str] = None
body_text: Optional[str] = None
created_at: Optional[str] = None
updated_at: Optional[str] = None


@dataclass
class TemplateResponse:
"""Envelope of a single-template response."""

data: Template


@dataclass
class TemplateListResponse:
"""Paginated response from listing templates."""

data: list[Template] = Field(default_factory=list)
pagination: Optional[Pagination] = None


@dataclass
class TemplateListParams(RequestParams):
"""
Query params for listing templates. ``token`` is the page number and
``per_page`` is capped at 100.
"""

per_page: Optional[int] = None
token: Optional[int] = None


@dataclass
class CreateTemplateParams(RequestParams):
"""Attributes for creating a template (sent as a flat JSON body)."""

name: str
subject: str
category: str
body_html: Optional[str] = None
body_text: Optional[str] = None


@dataclass
class UpdateTemplateParams(RequestParams):
"""
Attributes for updating a template (sent as a flat JSON body). All fields
are optional, but at least one must be provided.
"""

name: Optional[str] = None
subject: Optional[str] = None
category: Optional[str] = None
body_html: Optional[str] = None
body_text: Optional[str] = None

def __post_init__(self) -> None:
if all(
value is None
for value in [
self.name,
self.subject,
self.category,
self.body_html,
self.body_text,
]
):
raise ValueError("At least one field must be provided for update action")
Empty file.
Loading
Loading