diff --git a/Changelog.md b/Changelog.md index 1fcd23f18db925799a15aea64bc8f4e0a9a43d75..eb4cc34105655883a45956e83af95d3e14dbc6ae 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [0.33] - 2023-08-27 +- Fixed utf-8 issues with sending emails +- Fixed bug with applying migrations disabling all logging + ## [0.32] - 2023-08-27 - Send an email to the user on first login - Change NREN to (N)REN on the landing page diff --git a/compendium_v2/email/__init__.py b/compendium_v2/email/__init__.py index be3349efdb0a72d165cc5caa7a8615bfd0bd6ac5..c176f5199346c4b17206bf243ce2dd5aa3d24cd9 100644 --- a/compendium_v2/email/__init__.py +++ b/compendium_v2/email/__init__.py @@ -1,6 +1,8 @@ import smtplib import threading import logging +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText from typing import Sequence, Union from sqlalchemy import select from flask import current_app @@ -22,11 +24,11 @@ Daniel and Jennifer (the Compendium admins) """ # noqa: E501 -def _send_mail(smtp_server, port, sender_email, recipients, message): +def _send_mail(smtp_server: str, port: int, recipients: Union[str, Sequence[str]], message: MIMEMultipart): try: with smtplib.SMTP(smtp_server, port) as server: - server.sendmail(from_addr=sender_email, to_addrs=recipients, msg=message) + server.sendmail(from_addr=message['From'], to_addrs=recipients, msg=message.as_string()) logger.debug('Successfully sent email') except Exception: logger.exception('Unable to send email:') @@ -53,16 +55,18 @@ def send_mail( if not recipients: recipients = admin_emails - subject = subject.replace('\n', ' ') - message = f"""Subject: {subject}\n\n{contents}""" + message = MIMEMultipart('alternative') + message['Subject'] = subject + message['From'] = current_app.config['MAIL_SENDER_EMAIL'] + message['To'] = ', '.join(recipients) + message.attach(MIMEText(contents, 'plain', 'utf-8')) smtp_server = current_app.config['MAIL_SERVER'] port = current_app.config['MAIL_PORT'] - sender_email = current_app.config['MAIL_SENDER_EMAIL'] # spin off a thread since this can take some time.. logger.debug('Sending email') - thread = threading.Thread(target=_send_mail, args=(smtp_server, port, sender_email, recipients, message)) + thread = threading.Thread(target=_send_mail, args=(smtp_server, port, recipients, message)) thread.start() diff --git a/compendium_v2/environment.py b/compendium_v2/environment.py index fb8e8b7cdc575faba73a0685ba5950b3618cf415..d10241df6cf1c3c58001eef5b8021c829616e769 100644 --- a/compendium_v2/environment.py +++ b/compendium_v2/environment.py @@ -20,12 +20,16 @@ LOGGING_DEFAULT_CONFIG = { }, }, - 'loggers': { 'compendium_v2': { 'level': 'DEBUG', 'handlers': ['console'], 'propagate': False + }, + 'werkzeug': { + 'level': 'WARNING', + 'handlers': ['console'], + 'propagate': False } }, diff --git a/compendium_v2/migrations/env.py b/compendium_v2/migrations/env.py index e2408681ba289bd300144dddf1c47f754ba931d1..9683fdf42aefcbbc74693af36a38385861bf006a 100644 --- a/compendium_v2/migrations/env.py +++ b/compendium_v2/migrations/env.py @@ -1,5 +1,4 @@ import logging -from logging.config import fileConfig from flask import current_app @@ -9,10 +8,6 @@ from alembic import context # access to the values within the .ini file in use. config = context.config -# Interpret the config file for Python logging. -# This line sets up loggers basically. -if config.config_file_name is not None: - fileConfig(config.config_file_name) logger = logging.getLogger('alembic.env') diff --git a/setup.py b/setup.py index a4b2767488b4c29be01dddaae807006fb5b0b708..4ab1bc289bc3abdbb833fe41d375f7c16cf6ef99 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name='compendium-v2', - version="0.32", + version="0.33", author='GEANT', author_email='swd@geant.org', description='Flask and React project for displaying ' diff --git a/test/test_send_mail.py b/test/test_send_mail.py index e3ae5a9e448b4e1a2e11ab74d1624d64b49543d0..01bb6a755bbf47f25cd111d45685016637eb0373 100644 --- a/test/test_send_mail.py +++ b/test/test_send_mail.py @@ -1,8 +1,18 @@ +from typing import List +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText # noqa: F401 from contextlib import contextmanager from compendium_v2.db.auth_model import User from compendium_v2.email import send_admin_signup_notification, send_user_signup_notification +def decode_message(msg: MIMEMultipart): + message: List[MIMEText] = msg.get_payload() + text: MIMEText = message[0] + decoded = text.get_payload(decode=True).decode('utf-8') + return decoded + + @contextmanager def test_user(app): with app.app_context(): @@ -18,8 +28,10 @@ def test_user(app): def test_signup_email_admin(app, mocked_admin_user, mocker): def _send_mail(*args, **kwargs): + _msg = args[-1] + decoded = decode_message(_msg) message = 'testname has just signed up with the email testmail321@email.com and provider ID testsub' - assert args[-1].split('\n\n')[-1] == message + assert decoded == message mocker.patch('compendium_v2.email._send_mail', _send_mail) with test_user(app) as user: @@ -29,7 +41,9 @@ def test_signup_email_admin(app, mocked_admin_user, mocker): def test_signup_email_user(app, mocker): def _send_mail(*args, **kwargs): - assert len(args[-1].split('\n\n', 1)[-1]) > 50 # check that there's a message + _msg = args[-1] + decoded = decode_message(_msg) + assert len(decoded) > 50 # check that there's a message mocker.patch('compendium_v2.email._send_mail', _send_mail) with test_user(app) as user: