Skip to content

Commit

Permalink
feat: SMTP as a fallback function when Sendgrid quota exceeds limit (f…
Browse files Browse the repository at this point in the history
…ossasia#5981)

Modified arguments placement

Corrected quota info

Sent email and handled error

Cease email service if SMTP not configured

fix travis

divided into two tasks

changed logger message and smtp fallback
  • Loading branch information
mrsaicharan1 authored and iamareebjamal committed Aug 2, 2019
1 parent ff81819 commit 1fc75b0
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 65 deletions.
84 changes: 52 additions & 32 deletions app/api/helpers/mail.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import base64
import logging
from datetime import datetime

from flask import current_app
Expand All @@ -16,10 +17,28 @@
from app.models.user import User


def check_smtp_config(smtp_encryption):
"""
Checks config of SMTP
"""
config = {
'host': get_settings()['smtp_host'],
'username': get_settings()['smtp_username'],
'password': get_settings()['smtp_password'],
'encryption': smtp_encryption,
'port': get_settings()['smtp_port'],
}
for field in config:
if field is None:
return False
return True


def send_email(to, action, subject, html, attachments=None):
"""
Sends email and records it in DB
"""
from .tasks import send_email_task_sendgrid, send_email_task_smtp
if not string_empty(to):
email_service = get_settings()['email_service']
email_from_name = get_settings()['email_from_name']
Expand All @@ -36,40 +55,41 @@ def send_email(to, action, subject, html, attachments=None):
}

if not current_app.config['TESTING']:
if email_service == 'smtp':
smtp_encryption = get_settings()['smtp_encryption']
if smtp_encryption == 'tls':
smtp_encryption = 'required'
elif smtp_encryption == 'ssl':
smtp_encryption = 'ssl'
elif smtp_encryption == 'tls_optional':
smtp_encryption = 'optional'
else:
smtp_encryption = 'none'

config = {
'host': get_settings()['smtp_host'],
'username': get_settings()['smtp_username'],
'password': get_settings()['smtp_password'],
'encryption': smtp_encryption,
'port': get_settings()['smtp_port'],
}

from .tasks import send_mail_via_smtp_task
send_mail_via_smtp_task.delay(config, payload)
smtp_encryption = get_settings()['smtp_encryption']
if smtp_encryption == 'tls':
smtp_encryption = 'required'
elif smtp_encryption == 'ssl':
smtp_encryption = 'ssl'
elif smtp_encryption == 'tls_optional':
smtp_encryption = 'optional'
else:
payload['fromname'] = email_from_name
key = get_settings()['sendgrid_key']
if not key:
print('Sendgrid key not defined')
return
headers = {
"Authorization": ("Bearer " + key),
"Content-Type": "application/json"
}
from .tasks import send_email_task
send_email_task.delay(payload, headers)
smtp_encryption = 'none'

smtp_config = {
'host': get_settings()['smtp_host'],
'username': get_settings()['smtp_username'],
'password': get_settings()['smtp_password'],
'encryption': smtp_encryption,
'port': get_settings()['smtp_port'],
}
smtp_status = check_smtp_config(smtp_encryption)
if smtp_status:
if email_service == 'smtp':
send_email_task_smtp.delay(payload=payload, headers=None, smtp_config=smtp_config)
else:
key = get_settings().get('sendgrid_key')
if key:
headers = {
"Authorization": ("Bearer " + key),
"Content-Type": "application/json"
}
payload['fromname'] = email_from_name
send_email_task_sendgrid.delay(payload=payload, headers=headers, smtp_config=smtp_config)
else:
logging.exception('SMTP & sendgrid have not been configured properly')

else:
logging.exception('SMTP is not configured properly. Cannot send email.')
# record_mail(to, action, subject, html)
mail = Mail(
recipient=to, action=action, subject=subject,
Expand Down
72 changes: 39 additions & 33 deletions app/api/helpers/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from app.models.session import Session
from app.models.speaker import Speaker


"""
Define all API v2 celery tasks here
This is done to resolve circular imports
Expand Down Expand Up @@ -51,58 +52,63 @@
celery = make_celery()


@celery.task(name='send.email.post')
def send_email_task(payload, headers):
message = Mail(from_email=payload['from'],
to_emails=payload['to'],
subject=payload['subject'],
html_content=payload["html"])
if payload['attachments'] is not None:
for attachment in payload['attachments']:
with open(attachment, 'rb') as f:
file_data = f.read()
f.close()
encoded = base64.b64encode(file_data).decode()
attachment = Attachment()
attachment.file_content = FileContent(encoded)
attachment.file_type = FileType('application/pdf')
attachment.file_name = FileName(payload['to'])
attachment.disposition = Disposition('attachment')
message.add_attachment(attachment)
sendgrid_client = SendGridAPIClient(get_settings()['sendgrid_key'])
logging.info('Sending an email regarding {} on behalf of {}'.format(payload["subject"], payload["from"]))
@celery.task(name='send.email.post.sendgrid')
def send_email_task_sendgrid(payload, headers, smtp_config):
try:
message = Mail(from_email=payload['from'],
to_emails=payload['to'],
subject=payload['subject'],
html_content=payload["html"])
if payload['attachments'] is not None:
for attachment in payload['attachments']:
with open(attachment, 'rb') as f:
file_data = f.read()
f.close()
encoded = base64.b64encode(file_data).decode()
attachment = Attachment()
attachment.file_content = FileContent(encoded)
attachment.file_type = FileType('application/pdf')
attachment.file_name = FileName(payload['to'])
attachment.disposition = Disposition('attachment')
message.add_attachment(attachment)
sendgrid_client = SendGridAPIClient(get_settings()['sendgrid_key'])
logging.info('Sending an email regarding {} on behalf of {}'.format(payload["subject"], payload["from"]))
sendgrid_client.send(message)
logging.info('Email sent successfully')
except Exception:
logging.exception('Error occured while sending the email')
except urllib.error.HTTPError as e:
if e.code == 429:
logging.warning("Sendgrid quota has exceeded")
send_email_task_smtp.delay(payload=payload, headers=None, smtp_config=smtp_config)
else:
logging.exception("The following error has occurred with sendgrid-{}".format(str(e)))


@celery.task(name='send.email.post.smtp')
def send_mail_via_smtp_task(config, payload):
def send_email_task_smtp(payload, smtp_config, headers=None):
mailer_config = {
'transport': {
'use': 'smtp',
'host': config['host'],
'username': config['username'],
'password': config['password'],
'tls': config['encryption'],
'port': config['port']
'transport': {
'use': 'smtp',
'host': smtp_config['host'],
'username': smtp_config['username'],
'password': smtp_config['password'],
'tls': smtp_config['encryption'],
'port': smtp_config['port']
}
}
}

mailer = Mailer(mailer_config)
mailer.start()
message = Message(author=payload['from'], to=payload['to'])
message.subject = payload['subject']
message.plain = strip_tags(payload['html'])
message.rich = payload['html']
message.attach(name=payload['attachments'])
if payload['attachments'] is not None:
for attachment in payload['attachments']:
message.attach(name=attachment)
mailer.send(message)
logging.info('Message sent via SMTP')
mailer.stop()


@celery.task(base=RequestContextTask, name='resize.event.images', bind=True)
def resize_event_images_task(self, event_id, original_image_url):
event = safe_query(db, Event, 'id', event_id, 'event_id')
Expand Down

0 comments on commit 1fc75b0

Please sign in to comment.