Skip to content

Commit

Permalink
Merge pull request #882 from JoshuaOloton/fix/post-contact-us
Browse files Browse the repository at this point in the history
fix: post contact us
  • Loading branch information
Goketech authored Aug 15, 2024
2 parents e21ab1d + e28fef5 commit f534dce
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 2 deletions.
32 changes: 32 additions & 0 deletions api/core/dependencies/email/templates/admin-base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock %}</title>
</head>
<body style="margin: 0; padding: 0; font-family: Arial, sans-serif;">

<!-- Header -->
<table role="presentation" width="100%" style="background-color: #E1D6D6; height: 7.625rem; text-align: center;">
<tr>
<td>
<h2 style="color: #121a26; font-size: 1.5rem; font-weight: 600;">HNG Boilerplate</h2>
</td>
</tr>
</table>

{% block content %} {% endblock %}

<!-- Footer -->
<table role="presentation" width="100%" style="background-color: #F3EFEF; padding: 2rem 3rem; text-align: center;">
<tr>
<td>
<hr style="border: 1px dashed #969696; margin-bottom: 1.5rem;">
<p style="color: #5B5B5D; font-size: 0.875rem; font-weight: 400;">Please address this inquiry at your earliest convenience.</p>
<p style="color: #5B5B5D; font-size: 0.875rem; font-weight: 400;">You are receiving this email because a user submitted a contact form on our website.</p>
</td>
</tr>
</table>
</body>
</html>
57 changes: 57 additions & 0 deletions api/core/dependencies/email/templates/contact_us.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{% extends 'admin-base.html' %}

{% block title %}
Magic Link
{% endblock %}

{% block content %}
<div class="container"
style="
width: 100%;
max-width: 600px;
margin: auto;
padding: 20px;
font-family: Arial, sans-serif;
color: #333;
">
<div class="header"
style="
text-align: center;
padding-bottom: 20px;
">
<h1>Dear Admin,</h1>
<p>You have received a new contact form submission with the following details:</p>
</div>
<div class="content"
style="
margin-bottom: 20px;
">
<h3>User Details</h3>
<ul>
<li><span class="highlight"
style="
font-weight: bold; color: #333;
">
Full Name:</span> {{ full_name }}</li>
<li>
<span class="highlight"
style="
font-weight: bold; color: #333;
">
Email:</span> {{ email }}
</li>
<li>
<span class="highlight" style="
font-weight: bold; color: #333;
">
Phone Number:</span> {{ phone }}
</li>
</ul>
<h3>Message</h3>
<p class="message" style="margin-top: 10px;">
{{ message }}
</p>
</div>
</div>
{% endblock %}

29 changes: 29 additions & 0 deletions api/utils/send_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,32 @@ async def send_magic_link(context: dict):
with smtplib.SMTP_SSL(settings.MAIL_SERVER, settings.MAIL_PORT) as server:
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message.as_string())


async def send_contact_mail(context: dict):
"""Sends user contact to admin mail
Args:
context (dict): Holds data for sending email, such as 'name', 'email', and 'message'.
"""
from main import email_templates
sender_email = settings.MAIL_FROM
reciever_email = settings.MAIL_USERNAME
password = settings.MAIL_PASSWORD

html = email_templates.get_template("contact_us.html").render(context)

message = MIMEMultipart("alternative")
message["Subject"] = "New Contact Request"
message["From"] = sender_email
message["To"] = reciever_email

part = MIMEText(html, "html")

message.attach(part)

with smtplib.SMTP_SSL(settings.MAIL_SERVER, settings.MAIL_PORT) as server:
server.login(settings.MAIL_USERNAME, password)
server.sendmail(sender_email, reciever_email, message.as_string())


18 changes: 16 additions & 2 deletions api/v1/routes/contact_us.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from fastapi import APIRouter, Depends, status
from fastapi import APIRouter, Depends, status, BackgroundTasks
from sqlalchemy.orm import Session
from api.db.database import get_db
from api.utils.send_mail import send_contact_mail
from typing import Annotated
from api.core.responses import SUCCESS
from api.utils.success_response import success_response
Expand All @@ -25,10 +26,23 @@
},
)
async def create_contact_us(
data: CreateContactUs, db: Annotated[Session, Depends(get_db)]
data: CreateContactUs, db: Annotated[Session, Depends(get_db)],
background_tasks: BackgroundTasks,
):
"""Add a new contact us message."""
new_contact_us_message = contact_us_service.create(db, data)

# Send email to admin
background_tasks.add_task(
send_contact_mail,
context={
"full_name": new_contact_us_message.full_name,
"email": new_contact_us_message.email,
"phone": new_contact_us_message.title,
"message": new_contact_us_message.message,
}
)

response = success_response(
message=SUCCESS,
data={"id": new_contact_us_message.id},
Expand Down
1 change: 1 addition & 0 deletions api/v1/schemas/contact_us.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ class CreateContactUs(BaseModel):
email: EmailStr
phone_number: str
message: str
org_id: str
2 changes: 2 additions & 0 deletions api/v1/services/contact_us.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def __init__(self) -> None:
"email": "email",
"title": "phone_number", # Adapting the schema to the model
"message": "message",
"org_id": "org_id",
}
super().__init__()

Expand All @@ -28,6 +29,7 @@ def create(self, db: Annotated[Session, Depends(get_db)], data: CreateContactUs)
email=getattr(data, self.adabtingMapper["email"]),
title=getattr(data, self.adabtingMapper["title"]),
message=getattr(data, self.adabtingMapper["message"]),
org_id=getattr(data, self.adabtingMapper["org_id"])
)
db.add(contact_message)
db.commit()
Expand Down
107 changes: 107 additions & 0 deletions tests/v1/contact_us/test_post_contact_us.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from datetime import datetime, timezone
from unittest.mock import MagicMock, patch

import pytest
from fastapi.testclient import TestClient
from fastapi import status
from sqlalchemy.orm import Session
from uuid_extensions import uuid7

from api.db.database import get_db
from api.utils.send_mail import send_contact_mail
from api.v1.models.contact_us import ContactUs
from api.v1.models.organisation import Organisation
from api.v1.services.user import user_service
from api.v1.models.user import User
from main import app



@pytest.fixture
def db_session_mock():
db_session = MagicMock(spec=Session)
return db_session

@pytest.fixture
def client(db_session_mock):
app.dependency_overrides[get_db] = lambda: db_session_mock
client = TestClient(app)
yield client
app.dependency_overrides = {}

def mock_org():
return Organisation(
id=str(uuid7()),
name="Test Organisation",
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)

def mock_contact_us():
return ContactUs(
id=str(uuid7()),
full_name="Jane Doe",
email="jane.doe@example.com",
title="08058878456",
message="Hello, I would like more information about your services and pricing.",
org_id=mock_org().id
)

@patch('fastapi.BackgroundTasks.add_task')
@patch("api.v1.services.contact_us.contact_us_service.create")
def test_post_contact_us(mock_create, mock_add_task, db_session_mock, client):
'''Test to successfully create a new contact request'''

db_session_mock.add.return_value = None
db_session_mock.commit.return_value = None
db_session_mock.refresh.return_value = None

mock_create.return_value = mock_contact_us()
# mock_email_send.return_value = None

response = client.post('/api/v1/contact', json={
"full_name": "Jane Doe",
"email": "jane.doe@example.com",
"phone_number": "08058878456",
"message": "Hello, I would like more information about your services and pricing.",
"org_id": mock_org().id
})

print(response.json())
assert response.status_code == 201

# Assert that the contact_us_service.create was called with the expected arguments
mock_create.assert_called_once()

mock_add_task.assert_called_once()
mock_add_task.assert_called_with(
send_contact_mail,
context={
"full_name": "Jane Doe",
"email": "jane.doe@example.com",
"phone": "08058878456",
"message": "Hello, I would like more information about your services and pricing.",
}
)


@patch('fastapi.BackgroundTasks.add_task')
@patch("api.v1.services.contact_us.contact_us_service.create")
def test_post_contact_missing_fields(mock_create, mock_add_task, db_session_mock, client):
'''Test to unsuccessfully create a new contact request withz category'''

db_session_mock.add.return_value = None
db_session_mock.commit.return_value = None
db_session_mock.refresh.return_value = None

mock_create.return_value = mock_contact_us()

response = client.post('/api/v1/contact', json={
"email": "jane.doe@example.com",
"message": "Hello, I would like more information about your services and pricing.",
})

print(response.json())
assert response.status_code == 422


0 comments on commit f534dce

Please sign in to comment.