Skip to content

Commit

Permalink
Convert UserAccountEmail class to function
Browse files Browse the repository at this point in the history
This changes the logic to a function which can be imported and used in
the apps.

It also imports the external blueprint which has been moved to utils.
This means all logic can live in the function instead of having to pass
in the url as before.
  • Loading branch information
Wynndow committed Sep 22, 2017
1 parent cbb414c commit 8ab9bdc
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 59 deletions.
2 changes: 1 addition & 1 deletion dmutils/email/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

from .exceptions import EmailError
from .dm_notify import DMNotifyClient
from .user_account_email import UserAccountEmail
from .user_account_email import send_user_account_email
63 changes: 32 additions & 31 deletions dmutils/email/user_account_email.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
from flask import current_app, session, abort
from flask import current_app, session, abort, url_for
from . import DMNotifyClient, generate_token, EmailError
from .helpers import hash_string


class UserAccountEmail():
def send_user_account_email(role, email_address, template_id, extra_token_data={}, personalisation={}):
notify_client = DMNotifyClient(current_app.config['DM_NOTIFY_API_KEY'])

def __init__(self, token_data, template_id):
self.role = token_data['role']
self.email_address = token_data['email_address']
self.token = self._generate_create_token(token_data)
self.template_id = template_id
token_data = {
'role': role,
'email_address': email_address
}
token_data.update(extra_token_data)

def send_email(self, personalisation):
notify_client = DMNotifyClient(current_app.config['DM_NOTIFY_API_KEY'])
token = generate_token(
token_data,
current_app.config['SHARED_EMAIL_KEY'],
current_app.config['INVITE_EMAIL_SALT']
)

try:
notify_client.send_email(
self.email_address,
template_id=self.template_id,
personalisation=personalisation,
reference='create-user-account-{}'.format(hash_string(self.email_address))
)
session['email_sent_to'] = self.email_address
except EmailError as e:
current_app.logger.error(
"{code}: Create user email for email_hash {email_hash} failed to send. Error: {error}",
extra={
'error': str(e),
'email_hash': hash_string(self.email_address),
'code': '{}create.fail'.format(self.role)
})
abort(503, response="Failed to send user creation email.")
link_url = url_for('external.create_user', encoded_token=token, _external=True)

def _generate_create_token(self, token_data):
return generate_token(
token_data,
current_app.config['SHARED_EMAIL_KEY'],
current_app.config['INVITE_EMAIL_SALT']
personalisation.update({'url': link_url})

try:
notify_client.send_email(
email_address,
template_id=template_id,
personalisation=personalisation,
reference='create-user-account-{}'.format(hash_string(email_address))
)
session['email_sent_to'] = email_address
except EmailError as e:
current_app.logger.error(
"{code}: Create user email for email_hash {email_hash} failed to send. Error: {error}",
extra={
'error': str(e),
'email_hash': hash_string(email_address),
'code': '{}create.fail'.format(role)
})
abort(503, response="Failed to send user creation email.")
83 changes: 56 additions & 27 deletions tests/email/test_user_account_email.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import mock
import pytest
from flask import session
from flask import session, current_app

from dmutils.config import init_app
from dmutils.email import UserAccountEmail, EmailError
from dmutils.email import send_user_account_email, EmailError
from dmutils.email.tokens import decode_invitation_token
from dmutils.external import external as external_blueprint


@pytest.yield_fixture
def email_app(app):
init_app(app)
app.register_blueprint(external_blueprint)
app.config['SHARED_EMAIL_KEY'] = 'shared_email_key'
app.config['INVITE_EMAIL_SALT'] = 'invite_email_salt'
app.config['SECRET_KEY'] = 'secet_key'
Expand All @@ -18,41 +20,70 @@ def email_app(app):
yield app


class TestUserAccountEmail():
class TestSendUserAccountEmail():

def test_UserAccountEmail_object_creates_token_on_instantiation(self, email_app):
with email_app.app_context():
token_data = {
'role': 'buyer',
'email_address': '[email protected]'
}
@mock.patch('dmutils.email.user_account_email.generate_token')
@mock.patch('dmutils.email.user_account_email.DMNotifyClient')
def test_correctly_calls_notify_client_for_buyer(
self, DMNotifyClient, generate_token, email_app
):
with email_app.test_request_context():
generate_token.return_value = 'mocked-token'
notify_client_mock = mock.Mock()
DMNotifyClient.return_value = notify_client_mock

create_user_email = UserAccountEmail(token_data, 'notify-template-id')
send_user_account_email(
'buyer',
'[email protected]',
current_app.config['NOTIFY_TEMPLATES']['create_user_account']
)

assert decode_invitation_token(create_user_email.token) == {
'role': 'buyer',
'email_address': '[email protected]'
}
notify_client_mock.send_email.assert_called_once_with(
'[email protected]',
template_id='this-would-be-the-id-of-the-template',
personalisation={
'url': 'http://localhost/user/create/mocked-token'
},
reference='create-user-account-KmmJkEa5sLyv7vuxG3xja3S3fnnM6Rgq5EZY0S_kCjE='
)
assert session['email_sent_to'] == '[email protected]'

@mock.patch('dmutils.email.user_account_email.generate_token')
@mock.patch('dmutils.email.user_account_email.DMNotifyClient')
def test_send_email_correctly_calls_notify_client(self, DMNotifyClient, email_app):
def test_correctly_calls_notify_client_for_supplier(
self, DMNotifyClient, generate_token, email_app
):
with email_app.test_request_context():
generate_token.return_value = 'mocked-token'
notify_client_mock = mock.Mock()
DMNotifyClient.return_value = notify_client_mock

token_data = {
'role': 'buyer',
'role': 'supplier',
'email_address': '[email protected]'
}

create_buyer_email = UserAccountEmail(token_data, 'notify-template-id')
create_buyer_email.send_email({'url': 'http://link.to/create-user'})
send_user_account_email(
'supplier',
'[email protected]',
current_app.config['NOTIFY_TEMPLATES']['create_user_account'],
extra_token_data={
'supplier_id': 12345,
'supplier_name': 'Digital Marketplace'
},
personalisation={
'user': 'Digital Marketplace Team',
'supplier': 'Digital Marketplace'
}
)

notify_client_mock.send_email.assert_called_once_with(
'[email protected]',
template_id='notify-template-id',
template_id=current_app.config['NOTIFY_TEMPLATES']['create_user_account'],
personalisation={
'url': 'http://link.to/create-user'
'url': 'http://localhost/user/create/mocked-token',
'user': 'Digital Marketplace Team',
'supplier': 'Digital Marketplace'
},
reference='create-user-account-KmmJkEa5sLyv7vuxG3xja3S3fnnM6Rgq5EZY0S_kCjE='
)
Expand All @@ -67,13 +98,11 @@ def test_abort_with_503_if_send_email_fails_with_EmailError(self, DMNotifyClient
notify_client_mock.send_email.side_effect = EmailError('OMG!')
DMNotifyClient.return_value = notify_client_mock

token_data = {
'role': 'buyer',
'email_address': '[email protected]'
}

create_buyer_email = UserAccountEmail(token_data, 'notify-template-id')
create_buyer_email.send_email({'url': 'http://link.to/create-user'})
send_user_account_email(
'buyer',
'[email protected]',
current_app.config['NOTIFY_TEMPLATES']['create_user_account']
)

current_app.logger.error.assert_called_once_with(
"{code}: Create user email for email_hash {email_hash} failed to send. Error: {error}",
Expand Down

0 comments on commit 8ab9bdc

Please sign in to comment.