Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨(dimail) synchronize mailboxes from dimail to our db #453

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to

## [Unreleased]

### Added

- ✨(dimail) synchronize mailboxes from dimail to our db #453

### Fixed

- 🐛(frontend) fix update accesses form #448
Expand Down
97 changes: 97 additions & 0 deletions src/backend/mailbox_manager/tests/test_utils_dimail_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
Unit tests for dimail client
"""

import re

import pytest
import responses
from rest_framework import status

from mailbox_manager import enums, factories, models
from mailbox_manager.utils.dimail import DimailAPIClient

pytestmark = pytest.mark.django_db


def test_dimail_synchronization__already_sync():
"""
Nothing should be created when everything is already synced.
"""
domain = factories.MailDomainFactory(status=enums.MailDomainStatusChoices.ENABLED)
factories.MailboxFactory.create_batch(3, domain=domain)

pre_sync_mailboxes = models.Mailbox.objects.filter(domain=domain)
assert pre_sync_mailboxes.count() == 3

dimail_client = DimailAPIClient()
with responses.RequestsMock() as rsps:
# Ensure successful response using "responses":
rsps.add(
rsps.GET,
re.compile(r".*/token/"),
body='{"access_token": "dimail_people_token"}',
status=status.HTTP_200_OK,
content_type="application/json",
)
rsps.add(
rsps.GET,
re.compile(rf".*/domains/{domain.name}/mailboxes/"),
body=str(
[
{
"type": "mailbox",
"status": "broken",
"email": f"{mailbox.local_part}@{domain.name}",
"givenName": mailbox.first_name,
"surName": mailbox.last_name,
"displayName": f"{mailbox.first_name} {mailbox.last_name}",
}
for mailbox in pre_sync_mailboxes
]
),
status=status.HTTP_200_OK,
content_type="application/json",
)
dimail_client.synchronize_dimail_mailboxes(domain.name)

assert set(models.Mailbox.objects.filter(domain=domain)) == set(pre_sync_mailboxes)


def test_dimail_synchronization__synchronize_mailboxes():
"""A mailbox existing solely on dimail should be synchronized
upon calling sync function on its domain"""
domain = factories.MailDomainFactory(status=enums.MailDomainStatusChoices.ENABLED)
assert not models.Mailbox.objects.exists()

dimail_client = DimailAPIClient()
with responses.RequestsMock() as rsps:
# Ensure successful response using "responses":
rsps.add(
rsps.GET,
re.compile(r".*/token/"),
body='{"access_token": "dimail_people_token"}',
status=status.HTTP_200_OK,
content_type="application/json",
)
rsps.add(
rsps.GET,
re.compile(rf".*/domains/{domain.name}/mailboxes/"),
body=str(
[
{
"type": "mailbox",
"status": "broken",
"email": "[email protected]",
"givenName": "Admin",
"surName": "Context",
"displayName": "Context Admin",
}
]
),
status=status.HTTP_200_OK,
content_type="application/json",
)
dimail_client.synchronize_dimail_mailboxes(domain.name)

assert models.Mailbox.objects.exists()
52 changes: 52 additions & 0 deletions src/backend/mailbox_manager/utils/dimail.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""A minimalist client to synchronize with mailbox provisioning API."""

import ast
import smtplib
from logging import getLogger

Expand All @@ -13,6 +14,8 @@
from rest_framework import status
from urllib3.util import Retry

from mailbox_manager import models

logger = getLogger(__name__)

adapter = requests.adapters.HTTPAdapter(
Expand Down Expand Up @@ -163,3 +166,52 @@ def send_new_mailbox_notification(self, recipient, mailbox_data):
recipient,
exception,
)

def synchronize_dimail_mailboxes(self, domain_name):
"""Synchronize mailboxes from dimail - open xchange to our database.
This is useful in case of acquisition of a pre-existing mail domain.
This is not considered a new email and will not trigger any mail notification."""

domain = models.MailDomain.objects.get(name=domain_name)
people_mailboxes = models.Mailbox.objects.filter(domain=domain)

headers = self.get_headers()

try:
response = session.get(
f"{self.API_URL}/domains/{domain_name}/mailboxes/",
headers=headers,
verify=True,
timeout=10,
)
except requests.exceptions.ConnectionError as error:
logger.error(
"Connection error while trying to reach %s.",
self.API_URL,
exc_info=error,
)
raise error

if response.status_code != status.HTTP_200_OK:
return self.pass_dimail_unexpected_response(response)

content = ast.literal_eval(
response.content.decode("utf-8")
) # format output str to proper list

for mailbox in content:
local_part = mailbox["email"].split("@")[0]
if not mailbox["email"] in [str(mailbox) for mailbox in people_mailboxes]:
# creates a mailbox on our end
models.Mailbox.objects.create(
first_name=mailbox["givenName"],
last_name=mailbox["surName"],
local_part=local_part,
domain=domain,
secondary_email=mailbox[
"email"
], # secondary email is mandatory. Unfortunately, dimail doesn't store any.
# We temporarily give current email as secondary email.
)

return None
Loading