Skip to content

Commit

Permalink
refactor: Mail Alias
Browse files Browse the repository at this point in the history
  • Loading branch information
s-aga-r committed Jan 6, 2025
1 parent 455d081 commit 1b91fc5
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 82 deletions.
49 changes: 49 additions & 0 deletions mail/mail/doctype/mail_agent_job/mail_agent_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,52 @@ def delete_account_from_agents(email: str, agents: list[str] | None = None) -> N
agent_job.method = "DELETE"
agent_job.endpoint = f"/api/principal/{email}"
agent_job.insert()


def create_alias_on_agents(account: str, alias: str, agents: list[str] | None = None) -> None:
"""Creates an alias on all primary agents."""

primary_agents = agents or frappe.db.get_all(
"Mail Agent", filters={"enabled": 1, "is_primary": 1}, pluck="name"
)

if not primary_agents:
return

request_data = json.dumps([{"action": "addItem", "field": "emails", "value": alias}])
for agent in primary_agents:
agent_job = frappe.new_doc("Mail Agent Job")
agent_job.agent = agent
agent_job.method = "PATCH"
agent_job.endpoint = f"/api/principal/{account}"
agent_job.request_data = request_data
agent_job.insert()


def patch_alias_on_agents(
new_account: str, old_account: str, alias: str, agents: list[str] | None = None
) -> None:
"""Patches an alias on all primary agents."""

delete_account_from_agents(old_account, alias, agents)
create_alias_on_agents(new_account, alias, agents)


def delete_alias_from_agents(account: str, alias: str, agents: list[str] | None = None) -> None:
"""Deletes an alias from all primary agents."""

primary_agents = agents or frappe.db.get_all(
"Mail Agent", filters={"enabled": 1, "is_primary": 1}, pluck="name"
)

if not primary_agents:
return

request_data = json.dumps([{"action": "removeItem", "field": "emails", "value": alias}])
for agent in primary_agents:
agent_job = frappe.new_doc("Mail Agent Job")
agent_job.agent = agent
agent_job.method = "PATCH"
agent_job.endpoint = f"/api/principal/{account}"
agent_job.request_data = request_data
agent_job.insert()
35 changes: 5 additions & 30 deletions mail/mail/doctype/mail_alias/mail_alias.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,8 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

frappe.ui.form.on("Mail Alias", {
setup(frm) {
frm.trigger("set_queries");
},
// frappe.ui.form.on("Mail Alias", {
// refresh(frm) {

set_queries(frm) {
frm.set_query("domain_name", () => ({
filters: {
enabled: 1,
is_verified: 1,
},
}));

frm.set_query("mailbox", "mailboxes", (doc) => {
let filters = {
domain_name: doc.domain_name || " ",
incoming: 1,
};

let selected_mailboxes = frm.doc.mailboxes.map((d) => d.mailbox);
if (selected_mailboxes.length) {
filters.name = ["not in", selected_mailboxes];
}

return {
filters: filters,
};
});
},
});
// },
// });
55 changes: 34 additions & 21 deletions mail/mail/doctype/mail_alias/mail_alias.json
Original file line number Diff line number Diff line change
@@ -1,71 +1,82 @@
{
"actions": [],
"creation": "2024-04-06 08:33:50.603200",
"creation": "2025-01-06 12:19:09.415697",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"section_break_qoc3",
"enabled",
"section_break_ke92",
"section_break_uphe",
"alias_for_type",
"alias_for_name",
"column_break_wo8t",
"domain_name",
"column_break_7bls",
"alias",
"section_break_jpdi",
"mailboxes"
"email"
],
"fields": [
{
"fieldname": "section_break_qoc3",
"fieldtype": "Section Break"
},
{
"default": "1",
"depends_on": "eval: !doc.__islocal",
"fieldname": "enabled",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Enabled",
"search_index": 1
},
{
"fieldname": "domain_name",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Domain Name",
"no_copy": 1,
"options": "Mail Domain",
"reqd": 1,
"search_index": 1,
"set_only_once": 1
},
{
"depends_on": "eval: doc.__islocal",
"fieldname": "alias",
"fieldname": "email",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Alias",
"in_standard_filter": 1,
"label": "Email",
"no_copy": 1,
"options": "Email",
"reqd": 1,
"set_only_once": 1,
"unique": 1
},
{
"fieldname": "mailboxes",
"fieldtype": "Table",
"label": "Mailboxes",
"options": "Mail Alias Mailbox",
"reqd": 1
"fieldname": "section_break_uphe",
"fieldtype": "Section Break"
},
{
"fieldname": "section_break_ke92",
"fieldtype": "Section Break"
"fieldname": "alias_for_type",
"fieldtype": "Select",
"label": "Alias For (Type)",
"options": "\nMail Account",
"reqd": 1
},
{
"fieldname": "column_break_7bls",
"fieldtype": "Column Break"
"fieldname": "alias_for_name",
"fieldtype": "Dynamic Link",
"label": "Alias For (Name)",
"options": "alias_for_type",
"reqd": 1
},
{
"fieldname": "section_break_jpdi",
"fieldtype": "Section Break"
"fieldname": "column_break_wo8t",
"fieldtype": "Column Break"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-11-16 15:35:24.773591",
"modified": "2025-01-06 12:54:35.804872",
"modified_by": "Administrator",
"module": "Mail",
"name": "Mail Alias",
Expand All @@ -75,11 +86,13 @@
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
Expand Down
61 changes: 33 additions & 28 deletions mail/mail/doctype/mail_alias/mail_alias.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,54 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

import frappe
from frappe import _
from frappe.model.document import Document

from mail.utils.validation import (
is_valid_email_for_domain,
validate_domain_is_enabled_and_verified,
validate_mailbox_for_incoming,
from mail.mail.doctype.mail_agent_job.mail_agent_job import (
create_alias_on_agents,
delete_alias_from_agents,
patch_alias_on_agents,
)
from mail.utils.validation import is_valid_email_for_domain, validate_domain_is_enabled_and_verified


class MailAlias(Document):
def autoname(self) -> None:
self.alias = self.alias.strip().lower()
self.name = self.alias
self.email = self.email.strip().lower()
self.name = self.email

def validate(self) -> None:
self.validate_alias_for_name()
self.validate_domain()
self.validate_alias()
self.validate_mailboxes()
self.validate_email()

def validate_domain(self) -> None:
"""Validates the domain."""
def on_update(self) -> None:
if self.has_value_changed("email"):
create_alias_on_agents(self.alias_for_name, self.email)
return

validate_domain_is_enabled_and_verified(self.domain_name)
if self.has_value_changed("alias_for_name"):
patch_alias_on_agents(self.alias_for_name, self.get_doc_before_save().alias_for_name, self.email)

def validate_alias(self) -> None:
"""Validates the alias."""
def on_trash(self) -> None:
delete_alias_from_agents(self.alias_for_name, self.email)

is_valid_email_for_domain(self.alias, self.domain_name, raise_exception=True)
def validate_alias_for_name(self) -> None:
"""Validates the alias for name."""

def validate_mailboxes(self) -> None:
"""Validates the mailboxes."""
if not frappe.db.get_value(self.alias_for_type, self.alias_for_name, "enabled"):
frappe.throw(
frappe._("The {0} {1} is disabled.").format(
self.alias_for_type, frappe.bold(self.alias_for_name)
)
)

mailboxes = []
def validate_domain(self) -> None:
"""Validates the domain."""

for mailbox in self.mailboxes:
if mailbox.mailbox == self.alias:
frappe.throw(_("Row #{0}: Mailbox cannot be the same as the alias.").format(mailbox.idx))
elif mailbox.mailbox in mailboxes:
frappe.throw(
_("Row #{0}: Duplicate mailbox {1}.").format(mailbox.idx, frappe.bold(mailbox.mailbox))
)
validate_domain_is_enabled_and_verified(self.domain_name)

def validate_email(self) -> None:
"""Validates the email address."""

validate_mailbox_for_incoming(mailbox.mailbox)
mailboxes.append(mailbox.mailbox)
is_valid_email_for_domain(self.email, self.domain_name, raise_exception=True)
26 changes: 23 additions & 3 deletions mail/mail/doctype/mail_alias/test_mail_alias.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt

# import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.tests import IntegrationTestCase, UnitTestCase

# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
# Use these module variables to add/remove to/from that list
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]


class UnitTestMailAlias(UnitTestCase):
"""
Unit tests for MailAlias.
Use this class for testing individual functions and methods.
"""

pass


class IntegrationTestMailAlias(IntegrationTestCase):
"""
Integration tests for MailAlias.
Use this class for testing interactions between multiple components.
"""

class TestMailAlias(FrappeTestCase):
pass

0 comments on commit 1b91fc5

Please sign in to comment.