reworking of email sending
Emails now send at 3 different stages. Now we have two different types of email templates, one html, one plaintext. The templates, and reply email can be set actionview, per stage. Change-Id: I3a37959c547232c8f0df60a69953cc4ac7441d8e
This commit is contained in:
parent
ee43eb1c0c
commit
93c9806990
@ -1,3 +1,3 @@
|
||||
include README.md
|
||||
|
||||
graft stacktask/*/templates
|
||||
include stacktask/*/templates/*
|
||||
|
@ -48,19 +48,65 @@ DEFAULT_REGION: RegionOne
|
||||
# Additonal actions for views:
|
||||
# - The order of the actions matters. These will run after the default action,
|
||||
# in the given order.
|
||||
API_ACTIONS:
|
||||
ACTIONVIEW_SETTINGS:
|
||||
CreateProject:
|
||||
- AddAdminToProject
|
||||
- DefaultProjectResources
|
||||
actions:
|
||||
- AddAdminToProject
|
||||
- DefaultProjectResources
|
||||
emails:
|
||||
initial:
|
||||
subject: Initial Confirmation
|
||||
reply: no-reply@example.com
|
||||
template: initial.txt
|
||||
html_template: initial.txt
|
||||
# If the related actions 'can' send a token,
|
||||
# this field should here.
|
||||
token:
|
||||
subject: Your Token
|
||||
reply: no-reply@example.com
|
||||
template: token.txt
|
||||
html_template: token.txt
|
||||
completed:
|
||||
subject: Registration completed
|
||||
reply: no-reply@example.com
|
||||
template: completed.txt
|
||||
html_template: completed.txt
|
||||
AttachUser:
|
||||
emails:
|
||||
# To not send this email, set the value to null,
|
||||
# or don't have the field there at all.
|
||||
initial: null
|
||||
token:
|
||||
subject: Your Token
|
||||
reply: no-reply@example.com
|
||||
template: token.txt
|
||||
html_template: token.txt
|
||||
completed:
|
||||
subject: Registration completed
|
||||
reply: no-reply@example.com
|
||||
template: completed.txt
|
||||
html_template: completed.txt
|
||||
ResetPassword:
|
||||
emails:
|
||||
token:
|
||||
subject: Your Token
|
||||
reply: no-reply@example.com
|
||||
template: token.txt
|
||||
html_template: token.txt
|
||||
completed:
|
||||
subject: Registration completed
|
||||
reply: no-reply@example.com
|
||||
template: completed.txt
|
||||
html_template: completed.txt
|
||||
|
||||
# Action settings:
|
||||
ACTION_SETTINGS:
|
||||
DefaultProjectResources:
|
||||
"RegionOne":
|
||||
RegionOne:
|
||||
network_name: somenetwork
|
||||
subnet_name: somesubnet
|
||||
router_name: somerouter
|
||||
public_network: 83559fa7-0a67-4716-94b9-10596e3ed1e6
|
||||
public_network: 3cb50d61-5bce-4c03-96e6-8e262e12bb35
|
||||
DNS_NAMESERVERS:
|
||||
- 193.168.1.2
|
||||
- 193.168.1.3
|
||||
|
2
setup.py
2
setup.py
@ -4,7 +4,7 @@ from setuptools import setup, find_packages
|
||||
setup(
|
||||
name='stacktask',
|
||||
|
||||
version='0.1.0a3',
|
||||
version='0.1.0a4',
|
||||
description='A user registration service for openstack.',
|
||||
long_description=(
|
||||
'A registration service to sit alongside keystone and ' +
|
||||
|
@ -28,6 +28,7 @@ class Migration(migrations.Migration):
|
||||
('uuid', models.CharField(default=stacktask.api_v1.models.hex_uuid, max_length=200, serialize=False, primary_key=True)),
|
||||
('reg_ip', models.GenericIPAddressField()),
|
||||
('keystone_user', jsonfield.fields.JSONField(default={})),
|
||||
('action_view', models.CharField(max_length=200)),
|
||||
('action_notes', jsonfield.fields.JSONField(default={})),
|
||||
('approved', models.BooleanField(default=False)),
|
||||
('completed', models.BooleanField(default=False)),
|
||||
|
@ -34,6 +34,9 @@ class Registration(models.Model):
|
||||
reg_ip = models.GenericIPAddressField()
|
||||
keystone_user = JSONField(default={})
|
||||
|
||||
# which ActionView initiated this
|
||||
action_view = models.CharField(max_length=200)
|
||||
|
||||
# Effectively a log of what the actions are doing.
|
||||
action_notes = JSONField(default={})
|
||||
|
||||
|
11
stacktask/api_v1/templates/completed.txt
Normal file
11
stacktask/api_v1/templates/completed.txt
Normal file
@ -0,0 +1,11 @@
|
||||
Hello,
|
||||
|
||||
Your registration has been completed.
|
||||
|
||||
The actions you had requested are:
|
||||
{% for action in actions %}
|
||||
- {{ action }}
|
||||
{% endfor %}
|
||||
|
||||
Thank you for using our service.
|
||||
|
13
stacktask/api_v1/templates/initial.txt
Normal file
13
stacktask/api_v1/templates/initial.txt
Normal file
@ -0,0 +1,13 @@
|
||||
Hello,
|
||||
|
||||
Your registration is in our system and now waiting approval.
|
||||
|
||||
The actions you had requested are:
|
||||
{% for action in actions %}
|
||||
- {{ action }}
|
||||
{% endfor %}
|
||||
|
||||
Once someone has approved your registration you will be emailed an update.
|
||||
|
||||
Thank you for using our service.
|
||||
|
@ -80,44 +80,52 @@ def create_token(registration):
|
||||
return token
|
||||
|
||||
|
||||
def email_token(registration, token):
|
||||
def send_email(registration, email_conf, token=None):
|
||||
if email_conf:
|
||||
template = loader.get_template(email_conf['template'])
|
||||
html_template = loader.get_template(email_conf['html_template'])
|
||||
|
||||
emails = set()
|
||||
actions = []
|
||||
for action in registration.actions:
|
||||
act = action.get_action()
|
||||
if act.need_token:
|
||||
emails.add(act.token_email())
|
||||
actions.append(unicode(act))
|
||||
emails = set()
|
||||
actions = []
|
||||
for action in registration.actions:
|
||||
act = action.get_action()
|
||||
email = act.get_email()
|
||||
if email:
|
||||
emails.add(email)
|
||||
actions.append(unicode(act))
|
||||
|
||||
if len(emails) > 1:
|
||||
notes = {
|
||||
'notes':
|
||||
(("Error: Unable to send token, More than one email for" +
|
||||
" registration: %s") % registration.uuid)
|
||||
}
|
||||
create_notification(registration, notes)
|
||||
# TODO(adriant): raise some error?
|
||||
# and surround calls to this function with try/except
|
||||
if len(emails) > 1:
|
||||
notes = {
|
||||
'notes':
|
||||
(("Error: Unable to send token, More than one email for" +
|
||||
" registration: %s") % registration.uuid)
|
||||
}
|
||||
create_notification(registration, notes)
|
||||
return
|
||||
# TODO(adriant): raise some error?
|
||||
# and surround calls to this function with try/except
|
||||
|
||||
context = {'actions': actions, 'token': token.token}
|
||||
if token:
|
||||
context = {'registration': registration, 'actions': actions,
|
||||
'token': token.token}
|
||||
else:
|
||||
context = {'registration': registration, 'actions': actions}
|
||||
|
||||
email_template = loader.get_template("token.txt")
|
||||
|
||||
try:
|
||||
message = email_template.render(Context(context))
|
||||
send_mail(
|
||||
'Your token', message, 'no-reply@example.com',
|
||||
[emails.pop()], fail_silently=False)
|
||||
except SMTPException as e:
|
||||
notes = {
|
||||
'notes':
|
||||
("Error: '%s' while emailing token for registration: %s" %
|
||||
(e, registration.uuid))
|
||||
}
|
||||
create_notification(registration, notes)
|
||||
# TODO(adriant): raise some error?
|
||||
# and surround calls to this function with try/except
|
||||
try:
|
||||
message = template.render(Context(context))
|
||||
html_message = html_template.render(Context(context))
|
||||
send_mail(
|
||||
email_conf['subject'], message, email_conf['reply'],
|
||||
[emails.pop()], fail_silently=False, html_message=html_message)
|
||||
except SMTPException as e:
|
||||
notes = {
|
||||
'notes':
|
||||
("Error: '%s' while emailing token for registration: %s" %
|
||||
(e, registration.uuid))
|
||||
}
|
||||
create_notification(registration, notes)
|
||||
# TODO(adriant): raise some error?
|
||||
# and surround calls to this function with try/except
|
||||
|
||||
|
||||
def create_notification(registration, notes):
|
||||
@ -379,8 +387,38 @@ class RegistrationDetail(APIViewWithLogger):
|
||||
registration.save()
|
||||
if need_token:
|
||||
token = create_token(registration)
|
||||
email_token(registration, token)
|
||||
return Response({'notes': ['created token']}, status=200)
|
||||
try:
|
||||
class_conf = settings.ACTIONVIEW_SETTINGS[
|
||||
registration.action_view]
|
||||
|
||||
# will throw a key error if the token template has not
|
||||
# been specified
|
||||
email_conf = class_conf['emails']['token']
|
||||
send_email(registration, email_conf, token)
|
||||
return Response({'notes': ['created token']},
|
||||
status=200)
|
||||
except KeyError as e:
|
||||
notes = {
|
||||
'errors':
|
||||
[("Error: '%s' while sending " +
|
||||
"token. See registration " +
|
||||
"itself for details.") % e],
|
||||
'registration': registration.uuid
|
||||
}
|
||||
create_notification(registration, notes)
|
||||
|
||||
import traceback
|
||||
trace = traceback.format_exc()
|
||||
self.logger.critical(("(%s) - Exception escaped!" +
|
||||
" %s\n Trace: \n%s") %
|
||||
(timezone.now(), e, trace))
|
||||
|
||||
response_dict = {
|
||||
'errors':
|
||||
["Error: Something went wrong on the " +
|
||||
"server. It will be looked into shortly."]
|
||||
}
|
||||
return Response(response_dict, status=500)
|
||||
else:
|
||||
for action in actions:
|
||||
try:
|
||||
@ -406,6 +444,14 @@ class RegistrationDetail(APIViewWithLogger):
|
||||
registration.completed = True
|
||||
registration.completed_on = timezone.now()
|
||||
registration.save()
|
||||
|
||||
# Sending confirmation email:
|
||||
class_conf = settings.ACTIONVIEW_SETTINGS.get(
|
||||
registration.action_view, {})
|
||||
email_conf = class_conf.get(
|
||||
'emails', {}).get('completed', None)
|
||||
send_email(registration, email_conf)
|
||||
|
||||
return Response(
|
||||
{'notes': "Registration completed successfully."},
|
||||
status=200)
|
||||
@ -458,7 +504,36 @@ class TokenList(APIViewWithLogger):
|
||||
token.delete()
|
||||
|
||||
token = create_token(registration)
|
||||
email_token(registration, token)
|
||||
try:
|
||||
class_conf = settings.ACTIONVIEW_SETTINGS[
|
||||
registration.action_view]
|
||||
|
||||
# will throw a key error if the token template has not
|
||||
# been specified
|
||||
email_conf = class_conf['emails']['token']
|
||||
send_email(registration, email_conf, token)
|
||||
except KeyError as e:
|
||||
notes = {
|
||||
'errors':
|
||||
[("Error: '%s' while sending " +
|
||||
"token. See registration " +
|
||||
"itself for details.") % e],
|
||||
'registration': registration.uuid
|
||||
}
|
||||
create_notification(registration, notes)
|
||||
|
||||
import traceback
|
||||
trace = traceback.format_exc()
|
||||
self.logger.critical(("(%s) - Exception escaped!" +
|
||||
" %s\n Trace: \n%s") %
|
||||
(timezone.now(), e, trace))
|
||||
|
||||
response_dict = {
|
||||
'errors':
|
||||
["Error: Something went wrong on the " +
|
||||
"server. It will be looked into shortly."]
|
||||
}
|
||||
return Response(response_dict, status=500)
|
||||
return Response(
|
||||
{'notes': ['Token reissued.']}, status=200)
|
||||
|
||||
@ -584,6 +659,13 @@ class TokenDetail(APIViewWithLogger):
|
||||
token.registration.save()
|
||||
token.delete()
|
||||
|
||||
# Sending confirmation email:
|
||||
class_conf = settings.ACTIONVIEW_SETTINGS.get(
|
||||
token.registration.action_view, {})
|
||||
email_conf = class_conf.get(
|
||||
'emails', {}).get('completed', None)
|
||||
send_email(token.registration, email_conf)
|
||||
|
||||
return Response(
|
||||
{'notes': "Token submitted successfully."},
|
||||
status=200)
|
||||
@ -630,9 +712,12 @@ class ActionView(APIViewWithLogger):
|
||||
function on all the actions.
|
||||
"""
|
||||
|
||||
class_conf = settings.ACTIONVIEW_SETTINGS.get(self.__class__.__name__,
|
||||
{})
|
||||
|
||||
actions = [self.default_action, ]
|
||||
|
||||
actions += settings.API_ACTIONS.get(self.__class__.__name__, [])
|
||||
actions += class_conf.get('actions', [])
|
||||
|
||||
act_list = []
|
||||
|
||||
@ -658,7 +743,8 @@ class ActionView(APIViewWithLogger):
|
||||
keystone_user = request.keystone_user
|
||||
|
||||
registration = Registration.objects.create(
|
||||
reg_ip=ip_addr, keystone_user=keystone_user)
|
||||
reg_ip=ip_addr, keystone_user=keystone_user,
|
||||
action_view=self.__class__.__name__)
|
||||
registration.save()
|
||||
|
||||
for i, act in enumerate(act_list):
|
||||
@ -695,6 +781,10 @@ class ActionView(APIViewWithLogger):
|
||||
}
|
||||
return response_dict
|
||||
|
||||
# send initial conformation email:
|
||||
email_conf = class_conf.get('emails', {}).get('initial', None)
|
||||
send_email(registration, email_conf)
|
||||
|
||||
return {'registration': registration}
|
||||
else:
|
||||
errors = {}
|
||||
@ -759,8 +849,38 @@ class ActionView(APIViewWithLogger):
|
||||
if valid:
|
||||
if need_token:
|
||||
token = create_token(registration)
|
||||
email_token(registration, token)
|
||||
return Response({'notes': ['created token']}, status=200)
|
||||
try:
|
||||
class_conf = settings.ACTIONVIEW_SETTINGS[
|
||||
self.__class__.__name__]
|
||||
|
||||
# will throw a key error if the token template has not
|
||||
# been specified
|
||||
email_conf = class_conf['emails']['token']
|
||||
send_email(registration, email_conf, token)
|
||||
return Response({'notes': ['created token']},
|
||||
status=200)
|
||||
except KeyError as e:
|
||||
notes = {
|
||||
'errors':
|
||||
[("Error: '%s' while sending " +
|
||||
"token. See registration " +
|
||||
"itself for details.") % e],
|
||||
'registration': registration.uuid
|
||||
}
|
||||
create_notification(registration, notes)
|
||||
|
||||
import traceback
|
||||
trace = traceback.format_exc()
|
||||
self.logger.critical(("(%s) - Exception escaped!" +
|
||||
" %s\n Trace: \n%s") %
|
||||
(timezone.now(), e, trace))
|
||||
|
||||
response_dict = {
|
||||
'errors':
|
||||
["Error: Something went wrong on the " +
|
||||
"server. It will be looked into shortly."]
|
||||
}
|
||||
return Response(response_dict, status=500)
|
||||
else:
|
||||
for action in actions:
|
||||
try:
|
||||
@ -791,6 +911,13 @@ class ActionView(APIViewWithLogger):
|
||||
registration.completed = True
|
||||
registration.completed_on = timezone.now()
|
||||
registration.save()
|
||||
|
||||
# Sending confirmation email:
|
||||
class_conf = settings.ACTIONVIEW_SETTINGS.get(
|
||||
self.__class__.__name__, {})
|
||||
email_conf = class_conf.get(
|
||||
'emails', {}).get('completed', None)
|
||||
send_email(registration, email_conf)
|
||||
return Response(
|
||||
{'notes': "Registration completed successfully."},
|
||||
status=200)
|
||||
|
@ -128,11 +128,11 @@ class BaseAction(object):
|
||||
def need_token(self):
|
||||
return self.action.need_token
|
||||
|
||||
def token_email(self):
|
||||
return self._token_email()
|
||||
def get_email(self):
|
||||
return self._get_email()
|
||||
|
||||
def _token_email(self):
|
||||
raise NotImplementedError
|
||||
def _get_email(self):
|
||||
return None
|
||||
|
||||
def get_cache(self, key):
|
||||
return self.action.cache.get(key, None)
|
||||
@ -191,7 +191,7 @@ class UserAction(BaseAction):
|
||||
else:
|
||||
super(UserAction, self).__init__(*args, **kwargs)
|
||||
|
||||
def _token_email(self):
|
||||
def _get_email(self):
|
||||
return self.email
|
||||
|
||||
|
||||
|
@ -59,7 +59,7 @@ class IdentityManager(object):
|
||||
return role
|
||||
|
||||
def get_roles(self, user, project):
|
||||
return self.ks.roles.roles_for_user(user, tenant=project)
|
||||
return self.ks_client.roles.roles_for_user(user, tenant=project)
|
||||
|
||||
def add_user_role(self, user, role, project_id):
|
||||
self.ks_client.roles.add_user_role(user, role, project_id)
|
||||
|
@ -118,8 +118,7 @@ DEFAULT_REGION = CONFIG['DEFAULT_REGION']
|
||||
# Additonal actions for views:
|
||||
# - The order of the actions matters. These will run after the default action,
|
||||
# in the given order.
|
||||
API_ACTIONS = {'CreateProject': ['AddAdminToProject',
|
||||
'DefaultProjectResources']}
|
||||
ACTIONVIEW_SETTINGS = CONFIG['ACTIONVIEW_SETTINGS']
|
||||
|
||||
ACTION_SETTINGS = CONFIG['ACTION_SETTINGS']
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user