Skip to content

Commit

Permalink
refactor: Outbound API
Browse files Browse the repository at this point in the history
  • Loading branch information
s-aga-r committed Nov 15, 2024
1 parent af312f5 commit 248be4f
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 162 deletions.
68 changes: 2 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ These endpoints facilitate sending emails from the Frappe Mail Client.
- `in_reply_to_mail_name` (str | None): Optional reference ID for the email being replied to.
- `custom_headers` (dict | None): Optional custom headers.
- `attachments` (list[dict] | None): List of attachments.
- `is_newsletter` (bool): Optional flag to mark the email as a newsletter. Defaults to False.

**Response:** Returns a UUID (name) of the created Outgoing Mail.

Expand All @@ -242,6 +243,7 @@ These endpoints facilitate sending emails from the Frappe Mail Client.
- `from_` (str): Sender's email address.
- `to` (str | list[str]): Recipient email(s).
- `raw_message` (str): The complete raw MIME message.
- `is_newsletter` (bool): Optional flag to mark the email as a newsletter. Defaults to False.

**Response:** Returns the UUID (name) of the created Outgoing Mail.

Expand All @@ -251,72 +253,6 @@ These endpoints facilitate sending emails from the Frappe Mail Client.
{ "message": "019300a4-91fc-741f-9fe5-9ade8976637f" }
```

#### 2.3 Send Batch

**Endpoint:** `POST /outbound/send-batch` or `/api/method/mail.api.outbound.send_batch`

**Description:** Sends multiple email messages in a batch.

**Parameters:** JSON array of email details. Each entry contains `from_`, `to`, `subject`, etc., similar to `/outbound/send`.

**Response:** Returns a list of UUIDs (names) for the created Outgoing Mails.

**Example Response:**

```json
{
"message": [
"019300a4-91fc-741f-9fe5-9ade8976637f",
"019300a5-2062-7267-8966-603606f9b41b",
"01930081-3b5b-7ad4-a837-ff27f55bcbac"
]
}
```

#### 2.4 Send Raw Batch

**Endpoint:** `POST /outbound/send-raw-batch` or `/api/method/mail.api.outbound.send_raw_batch`

**Description:** Sends multiple raw MIME messages in a batch.

**Parameters:** JSON array of `from_`, `to`, `raw_message`, similar to `/outbound/send-raw`.

**Response:** Returns a list of UUIDs (names) for the created Outgoing Mails.

**Example Response:**

```json
{
"message": [
"019300a4-91fc-741f-9fe5-9ade8976637f",
"019300a5-2062-7267-8966-603606f9b41b",
"01930081-3b5b-7ad4-a837-ff27f55bcbac"
]
}
```

#### 2.5 Send Newsletter

**Endpoint:** `POST /outbound/send-newsletter` or `/api/method/mail.api.outbound.send_newsletter`

**Description:** Sends low-priority newsletters. Works the same as batch email sending but with a priority setting.

**Parameters:** JSON array of newsletter details similar to `/outbound/send-batch` or `/outbound/send-raw-batch`.

**Response:** Returns a list of UUIDs (names) for the created newsletter Outgoing Mails.

**Example Response:**

```json
{
"message": [
"019300a4-91fc-741f-9fe5-9ade8976637f",
"019300a5-2062-7267-8966-603606f9b41b",
"01930081-3b5b-7ad4-a837-ff27f55bcbac"
]
}
```

### 3. Inbound API

These endpoints are for retrieving received emails.
Expand Down
115 changes: 34 additions & 81 deletions mail_client/api/outbound.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import json
from email.utils import parseaddr

import frappe
from frappe import _
from frappe.utils import cint

from mail_client.mail_client.doctype.outgoing_mail.outgoing_mail import create_outgoing_mail

Expand All @@ -20,6 +20,7 @@ def send(
in_reply_to_mail_name: str | None = None,
custom_headers: dict | None = None,
attachments: list[dict] | None = None,
is_newsletter: bool = False,
) -> str:
"""Send Mail."""

Expand All @@ -38,6 +39,7 @@ def send(
custom_headers=custom_headers,
attachments=attachments,
via_api=1,
is_newsletter=cint(is_newsletter),
)

return doc.name
Expand All @@ -47,107 +49,58 @@ def send(
def send_raw(
from_: str,
to: str | list[str],
raw_message: str,
raw_message: str | None = None,
is_newsletter: bool = False,
) -> str:
"""Send Raw Mail."""

display_name, sender = parseaddr(from_)
raw_message = raw_message or get_message_from_files()
if not raw_message:
frappe.throw(_("The raw message is required."), frappe.MandatoryError)

doc = create_outgoing_mail(
sender=sender,
to=to,
display_name=display_name,
raw_message=raw_message,
via_api=1,
is_newsletter=cint(is_newsletter),
)

return doc.name


# TODO: Remove
@frappe.whitelist(methods=["POST"])
def send_batch() -> list[str]:
"""Send Mails in Batch."""

mails = json.loads(frappe.request.data.decode())
validate_batch(mails, mandatory_fields=["from_", "to", "subject"])

documents = []
for mail in mails:
mail = get_mail_dict(mail)
doc = create_outgoing_mail(**mail)
documents.append(doc.name)

return documents


@frappe.whitelist(methods=["POST"])
def send_raw_batch() -> list[str]:
"""Send Raw Mails in Batch."""

mails = json.loads(frappe.request.data.decode())
validate_batch(mails, mandatory_fields=["from_", "to", "raw_message"])

documents = []
for mail in mails:
mail = get_mail_dict(mail)
doc = create_outgoing_mail(**mail)
documents.append(doc.name)

return documents


@frappe.whitelist(methods=["POST"])
def send_newsletter() -> None:
def send_newsletter(
from_: str,
to: str | list[str],
raw_message: str | None = None,
) -> str:
"""Send Newsletter."""

mails = json.loads(frappe.request.data.decode())

if isinstance(mails, dict):
mails = [mails]

validate_batch(mails, mandatory_fields=["from_", "to"])

for mail in mails:
mail = get_mail_dict(mail)
mail["is_newsletter"] = 1
create_outgoing_mail(**mail)


def validate_batch(mails: list[dict], mandatory_fields: list[str]) -> None:
"""Validates the batch data."""

if len(mails) > 100:
raise frappe.ValidationError("Batch size cannot exceed 100.")
display_name, sender = parseaddr(from_)
raw_message = raw_message or get_message_from_files()
if not raw_message:
frappe.throw(_("The raw message is required."), frappe.MandatoryError)

for mail in mails:
for field in mandatory_fields:
if not mail.get(field):
raise frappe.ValidationError(f"{field} is mandatory.")
doc = create_outgoing_mail(
sender=sender,
to=to,
display_name=display_name,
raw_message=raw_message,
via_api=1,
is_newsletter=1,
)

return doc.name

def get_mail_dict(data: dict) -> dict:
"""Returns the mail dict."""

display_name, sender = parseaddr(data["from_"])
mail = {
"sender": sender,
"to": data["to"],
"display_name": display_name,
"via_api": 1,
}
def get_message_from_files() -> str | None:
"""Returns the message from the files"""

if data.get("raw_message"):
mail["raw_message"] = data["raw_message"]
else:
mail.update(
{
"subject": data["subject"],
"cc": data.get("cc"),
"bcc": data.get("bcc"),
"body_html": data.get("html"),
"reply_to": data.get("reply_to"),
"custom_headers": data.get("headers"),
"attachments": data.get("attachments"),
}
)
files = frappe._dict(frappe.request.files)

return mail
if files and files.get("raw_message"):
return files["raw_message"].read().decode("utf-8")
15 changes: 0 additions & 15 deletions mail_client/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,6 @@
"target": "/api/method/mail_client.api.outbound.send_raw",
"redirect_http_status": 307,
},
{
"source": "/outbound/send-batch",
"target": "/api/method/mail_client.api.outbound.send_batch",
"redirect_http_status": 307,
},
{
"source": "/outbound/send-raw-batch",
"target": "/api/method/mail_client.api.outbound.send_raw_batch",
"redirect_http_status": 307,
},
{
"source": "/outbound/send-newsletter",
"target": "/api/method/mail_client.api.outbound.send_newsletter",
"redirect_http_status": 307,
},
{
"source": "/inbound/pull",
"target": "/api/method/mail_client.api.inbound.pull",
Expand Down

0 comments on commit 248be4f

Please sign in to comment.