Notification code finished with email function verified

This commit is contained in:
jetli 2024-10-25 22:37:47 +00:00
parent 9c4baae49e
commit 6a9b876b71
14 changed files with 96 additions and 46 deletions

View File

@ -7,6 +7,7 @@ loguru==0.7.2
uvicorn==0.23.2 uvicorn==0.23.2
beanie==1.21.0 beanie==1.21.0
jieba==0.42.1 jieba==0.42.1
sendgrid
aio-pika aio-pika
httpx httpx
pydantic-settings pydantic-settings

View File

@ -6,7 +6,8 @@ from app.authentication.webapi.providers import common
from app.authentication.webapi.providers import logger from app.authentication.webapi.providers import logger
from app.authentication.webapi.providers import router from app.authentication.webapi.providers import router
from app.authentication.webapi.providers import database from app.authentication.webapi.providers import database
from app.authentication.webapi.providers import scheduler
# from app.authentication.webapi.providers import scheduler
from app.authentication.webapi.providers import exception_handler from app.authentication.webapi.providers import exception_handler
from .freeleaps_app import FreeleapsApp from .freeleaps_app import FreeleapsApp
@ -20,7 +21,7 @@ def create_app() -> FastAPI:
register(app, database) register(app, database)
register(app, logger) register(app, logger)
register(app, router) register(app, router)
register(app, scheduler) # register(app, scheduler)
register(app, common) register(app, common)
# Call the custom_openapi function to change the OpenAPI version # Call the custom_openapi function to change the OpenAPI version

View File

@ -9,19 +9,19 @@ from pydantic import BaseModel
router = APIRouter() router = APIRouter()
class Item(BaseModel): class Item(BaseModel):
associated_with: str associated_with: str
name: str name: str
blob: bytes blob: bytes
@router.post( @router.post(
"/upload-document", "/upload-document",
summary="upload a document with a given associated_with id, document name and document data.", summary="upload a document with a given associated_with id, document name and document data.",
description="upload a document. If success, returning the document id", description="upload a document. If success, returning the document id",
) )
async def upload_document( async def upload_document(item: Item):
item:Item
):
document_hub = DocumentHub() document_hub = DocumentHub()
# File processing # File processing
@ -39,5 +39,4 @@ async def upload_document(
) )
except Exception as e: except Exception as e:
print("this is exception", e)
return JSONResponse(status_code=500, content={"error": "Internal server error"}) return JSONResponse(status_code=500, content={"error": "Internal server error"})

View File

@ -16,10 +16,7 @@ token_manager = TokenManager()
summary="upload a document with a given associated_with id.", summary="upload a document with a given associated_with id.",
description="upload a document. If success, returning the document id and file name", description="upload a document. If success, returning the document id and file name",
) )
async def upload_file( async def upload_file(associated_with: str = Form(...), file: UploadFile = File(None)):
associated_with: str = Form(...),
file: UploadFile = File(None)
):
document_hub = DocumentHub() document_hub = DocumentHub()
# File processing # File processing
@ -38,5 +35,4 @@ async def upload_file(
) )
except Exception as e: except Exception as e:
print("this is exception", e)
return JSONResponse(status_code=500, content={"error": "Internal server error"}) return JSONResponse(status_code=500, content={"error": "Internal server error"})

View File

@ -15,9 +15,6 @@ from datetime import datetime, timezone
from typing import Optional, Type from typing import Optional, Type
from types import TracebackType from types import TracebackType
from infra.models.constants import UserRegion from infra.models.constants import UserRegion
from app.notification.backend.services.notification_publisher_service import (
NotificationPublisherService,
)
from app.notification.common.config.app_settings import app_settings from app.notification.common.config.app_settings import app_settings
from datetime import datetime, timezone from datetime import datetime, timezone
@ -31,7 +28,6 @@ class NotificationManager:
self, self,
sender_id: str, # Require sender_id in the constructor sender_id: str, # Require sender_id in the constructor
) -> None: ) -> None:
self.notification_publisher_service = NotificationPublisherService()
self.sms_service = SmsService() self.sms_service = SmsService()
self.in_app_notif_service = InAppNotifService() self.in_app_notif_service = InAppNotifService()
self.email_service = EmailService() self.email_service = EmailService()
@ -172,13 +168,27 @@ class NotificationManager:
properties_dict = { properties_dict = {
"content_text": content_text, "content_text": content_text,
"content_subject": content_subject, "content_subject": content_subject,
"sender_email": app_settings.EMAIL_FROM, "sender_email": "qifei.lu1994@gmail.com",
"receiver_type": "email", "receiver_type": "email",
} }
await self.__publish_notification__( await self.__publish_notification__(
channel=NotificationChannel.EMAIL, channel=NotificationChannel.EMAIL,
message=NotificationMessage( message=NotificationMessage(
sender_id=app_settings.SYSTEM_USER_ID, sender_id="aaabbbcccddd", # app_settings.SYSTEM_USER_ID,
receiver_id=receiver_id,
subject=subject,
event=event,
properties=(
properties_dict.update(properties)
if not properties
else properties_dict
),
),
)
print(
"this is message",
NotificationMessage(
sender_id="aaabbbcccddd", # app_settings.SYSTEM_USER_ID,
receiver_id=receiver_id, receiver_id=receiver_id,
subject=subject, subject=subject,
event=event, event=event,
@ -244,6 +254,7 @@ class NotificationManager:
properties=properties, properties=properties,
) )
elif channel == NotificationChannel.EMAIL: elif channel == NotificationChannel.EMAIL:
print("should get here")
await self.send_email_notification( await self.send_email_notification(
receiver_id=receiver_id, receiver_id=receiver_id,
subject=subject, subject=subject,

View File

@ -10,7 +10,6 @@ class EmailHandler:
async def send_email(self, message: dict): async def send_email(self, message: dict):
receiver_id = message["receiver_id"] receiver_id = message["receiver_id"]
sender_email = message["properties"]["sender_email"]
content_text = message["properties"]["content_text"] content_text = message["properties"]["content_text"]
content_subject = message["properties"]["content_subject"] content_subject = message["properties"]["content_subject"]
receiver_type = message["properties"]["receiver_type"] receiver_type = message["properties"]["receiver_type"]
@ -25,12 +24,11 @@ class EmailHandler:
return return
mail = Mail( mail = Mail(
from_email=sender_email, from_email=app_settings.EMAIL_FROM,
to_emails=receiver_email, to_emails=receiver_email,
subject=content_subject, subject=content_subject,
html_content=content_text, html_content=content_text,
) )
try: try:
sg = SendGridAPIClient(app_settings.SENDGRID_API_KEY) sg = SendGridAPIClient(app_settings.SENDGRID_API_KEY)
response = sg.send(mail) response = sg.send(mail)

View File

@ -25,8 +25,7 @@ class AsyncMQClient:
while retry_count < max_retries: while retry_count < max_retries:
try: try:
self.connection = await aio_pika.connect_robust( self.connection = await aio_pika.connect_robust(
host=app_settings.RABBITMQ_HOST, "amqp://guest:guest@rabbitmq:5672/",
port=int(app_settings.RABBITMQ_PORT),
loop=event_loop, loop=event_loop,
) )
self.channel = await self.connection.channel() self.channel = await self.connection.channel()

View File

@ -8,14 +8,16 @@ class AppSettings(BaseSettings):
RABBITMQ_HOST: str = "rabbitmq" RABBITMQ_HOST: str = "rabbitmq"
RABBITMQ_PORT: int = 5672 RABBITMQ_PORT: int = 5672
SYSTEM_USER_ID: str = "" SYSTEM_USER_ID: str = "117f191e810c19729de860aa"
SMS_FROM: str = "" SMS_FROM: str = "DDDDD"
EMAIL_FROM: str = "" EMAIL_FROM: str = "qifei.lu1994@gmail.com"
SENDGRID_API_KEY: str = "" SENDGRID_API_KEY: str = (
"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"
)
TWILIO_ACCOUNT_SID: str = "" TWILIO_ACCOUNT_SID: str = "ACf8c9283a6acda060258eadb29be58bc8"
TWILIO_AUTH_TOKEN: str = "" TWILIO_AUTH_TOKEN: str = "ef160748cc22c8b7195b49df4b8eca7e"
class Config: class Config:
env_file = ".myapp.env" env_file = ".myapp.env"

View File

@ -7,6 +7,7 @@ from app.notification.webapi.providers import logger
from app.notification.webapi.providers import router from app.notification.webapi.providers import router
from app.notification.webapi.providers import database from app.notification.webapi.providers import database
from app.notification.webapi.providers import scheduler from app.notification.webapi.providers import scheduler
from app.notification.webapi.providers import message_queue
from app.notification.webapi.providers import exception_handler from app.notification.webapi.providers import exception_handler
from .freeleaps_app import FreeleapsApp from .freeleaps_app import FreeleapsApp
@ -22,6 +23,7 @@ def create_app() -> FastAPI:
register(app, router) register(app, router)
register(app, scheduler) register(app, scheduler)
register(app, common) register(app, common)
register(app, message_queue)
# Call the custom_openapi function to change the OpenAPI version # Call the custom_openapi function to change the OpenAPI version
customize_openapi_security(app) customize_openapi_security(app)

View File

@ -13,3 +13,18 @@ class FreeleapsApp(FastAPI):
self.in_app_mq_client = AsyncMQSubscriber(NotificationChannel.IN_APP.name) self.in_app_mq_client = AsyncMQSubscriber(NotificationChannel.IN_APP.name)
self.email_handler = EmailMQConsumer(self.email_mq_client) self.email_handler = EmailMQConsumer(self.email_mq_client)
self.sms_handler = SmsMQConsumer(self.sms_mq_client) self.sms_handler = SmsMQConsumer(self.sms_mq_client)
# Register the consumers on startup and shutdown
print("Registering startup/shutdown events") # Debugging line
self.register_startup_shutdown_events()
def register_startup_shutdown_events(self):
@self.on_event("startup")
async def start_consumers():
print("starting up!")
await self.sms_handler.register_consumer()
await self.email_handler.register_consumer()
@self.on_event("shutdown")
async def stop_consumers():
await self.sms_handler.unregister_consumer()
await self.email_handler.unregister_consumer()

View File

@ -0,0 +1,23 @@
import asyncio
def register(app):
@app.on_event("startup")
async def start_consumers():
loop = asyncio.get_running_loop()
await loop.create_task(
app.in_app_mq_client.subscribe(max_retries=5, event_loop=loop)
)
await loop.create_task(
app.email_mq_client.subscribe(max_retries=5, event_loop=loop)
)
await loop.create_task(
app.sms_mq_client.subscribe(max_retries=5, event_loop=loop)
)
@app.on_event("shutdown")
async def stop_consumers():
loop = asyncio.get_running_loop()
await loop.create_task(app.in_app_mq_client.close())
await loop.create_task(app.email_mq_client.close())
await loop.create_task(app.sms_mq_client.close())

View File

@ -1,4 +1,5 @@
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends, HTTPException
import traceback
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR
from app.notification.backend.application.notification_hub import NotificationHub from app.notification.backend.application.notification_hub import NotificationHub
@ -30,11 +31,11 @@ class NotificationRequest(BaseModel):
) )
# API route to enqueue notifications # API route to enqueue notifications
@router.post("/send_notification") @router.post("/send_notification")
async def enqueue_notification(request: NotificationRequest): async def send_notification(request: NotificationRequest):
try: try:
notification_hub = NotificationHub() notification_hub = NotificationHub()
await notification_hub.enqueue_notification( await notification_hub.enqueue_notification(
sender_id=request.sender_id, # Pass sender_id sender_id="freeleaps@freeleaps.com",
channels=request.channels, channels=request.channels,
receiver_id=request.receiver_id, receiver_id=request.receiver_id,
subject=request.subject, subject=request.subject,

View File

@ -6,8 +6,7 @@ from app.notification.backend.infra.email_handler import EmailHandler
class EmailMQConsumer: class EmailMQConsumer:
@staticmethod @staticmethod
async def mail_handler(register_key: str, message: dict, args: dict): async def mail_handler(register_key: str, message: dict, args: dict):
email_consumer: EmailMQConsumer = args["message_handler"] return await EmailHandler().send_email(message)
return await email_consumer.email_handler.send_email(message)
def __init__(self, mq_client: AsyncMQSubscriber) -> None: def __init__(self, mq_client: AsyncMQSubscriber) -> None:
self.sender_id = app_settings.EMAIL_FROM self.sender_id = app_settings.EMAIL_FROM
@ -16,7 +15,7 @@ class EmailMQConsumer:
async def register_consumer(self): async def register_consumer(self):
await self.mq_client.register_consumer( await self.mq_client.register_consumer(
registry_key=self.sender_id, registry_key=app_settings.EMAIL_FROM,
callback_method=EmailMQConsumer.mail_handler, callback_method=EmailMQConsumer.mail_handler,
args={"email_handler": self}, args={"email_handler": self},
) )

View File

@ -1,17 +1,17 @@
services: services:
# rabbitmq: rabbitmq:
# image: rabbitmq:3-management image: rabbitmq:3-management
# ports: ports:
# - "5672:5672" # RabbitMQ communication port - "5672:5672" # RabbitMQ communication port
# - "15672:15672" # RabbitMQ management port - "15672:15672" # RabbitMQ management port
# networks: networks:
# - freeleaps_service_hub_network - freeleaps_service_hub_network
# healthcheck: healthcheck:
# test: [ "CMD", "rabbitmq-diagnostics", "ping" ] test: [ "CMD", "rabbitmq-diagnostics", "ping" ]
# interval: 30s interval: 30s
# retries: 5 retries: 5
# start_period: 10s start_period: 10s
# timeout: 10s timeout: 10s
central_storage: central_storage:
build: build:
@ -54,6 +54,9 @@ services:
- freeleaps_service_hub_network - freeleaps_service_hub_network
env_file: env_file:
- sites/notification/.env - sites/notification/.env
environment:
RABBITMQ_HOST: rabbitmq
SENDGRID_API_KEY: "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"
volumes: volumes:
- .:/app # Mount the current directory to /app in the container - .:/app # Mount the current directory to /app in the container