diff --git a/api/core/dependencies/email/templates/admin-base.html b/api/core/dependencies/email/templates/admin-base.html
new file mode 100644
index 000000000..8f73452ef
--- /dev/null
+++ b/api/core/dependencies/email/templates/admin-base.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+ {% block title %}{% endblock %}
+
+
+
+
+
+
+
+ HNG Boilerplate
+ |
+
+
+
+ {% block content %} {% endblock %}
+
+
+
+
+
+
+ Please address this inquiry at your earliest convenience.
+ You are receiving this email because a user submitted a contact form on our website.
+ |
+
+
+
+
\ No newline at end of file
diff --git a/api/core/dependencies/email/templates/contact_us.html b/api/core/dependencies/email/templates/contact_us.html
new file mode 100644
index 000000000..626fe1137
--- /dev/null
+++ b/api/core/dependencies/email/templates/contact_us.html
@@ -0,0 +1,57 @@
+{% extends 'admin-base.html' %}
+
+{% block title %}
+ Magic Link
+{% endblock %}
+
+{% block content %}
+
+
+
+
User Details
+
+ -
+ Full Name: {{ full_name }}
+ -
+
+ Email: {{ email }}
+
+ -
+
+ Phone Number: {{ phone }}
+
+
+
Message
+
+ {{ message }}
+
+
+
+{% endblock %}
+
diff --git a/api/utils/send_mail.py b/api/utils/send_mail.py
index f85415a51..e4d2c2fec 100644
--- a/api/utils/send_mail.py
+++ b/api/utils/send_mail.py
@@ -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())
+
+
diff --git a/api/v1/routes/contact_us.py b/api/v1/routes/contact_us.py
index f9312b861..6620c9c51 100644
--- a/api/v1/routes/contact_us.py
+++ b/api/v1/routes/contact_us.py
@@ -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
@@ -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},
diff --git a/api/v1/schemas/contact_us.py b/api/v1/schemas/contact_us.py
index f07969f22..2123b6b02 100644
--- a/api/v1/schemas/contact_us.py
+++ b/api/v1/schemas/contact_us.py
@@ -18,3 +18,4 @@ class CreateContactUs(BaseModel):
email: EmailStr
phone_number: str
message: str
+ org_id: str
diff --git a/api/v1/services/contact_us.py b/api/v1/services/contact_us.py
index 936ea267c..b95740a19 100644
--- a/api/v1/services/contact_us.py
+++ b/api/v1/services/contact_us.py
@@ -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__()
@@ -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()
diff --git a/tests/v1/contact_us/test_post_contact_us.py b/tests/v1/contact_us/test_post_contact_us.py
new file mode 100644
index 000000000..485af38a3
--- /dev/null
+++ b/tests/v1/contact_us/test_post_contact_us.py
@@ -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
+
+