Additional Emails - Additional Action
Currently set up as an additional action added to the task in the configuration file. At each stage (corresponding to the current email section lables) a template and subject can be specified detailing the email to be sent. This will be sent to the users email address or otherwise an override email address set from the task. In the configuration sending to the users email address can be turned off with the line email_current_user: false Additionally an email can be sent out to a group of roles within a project using: email_roles: - project_admin Or to a number of specific emails: email_additional_addresses: - admin@example.org Or to an address specified in the task cache email_in_task_cache: true (Cache key "additional_emails") Change-Id: I6d454bdfefb7549322fea6cf0c91fac76b5aa89a
This commit is contained in:
parent
890b154591
commit
58ac750bcc
@ -186,6 +186,34 @@ DEFAULT_ACTION_SETTINGS:
|
||||
regions:
|
||||
RegionOne:
|
||||
quota_size: small
|
||||
SendAdditionalEmailAction:
|
||||
initial:
|
||||
email_current_user: false
|
||||
reply: no-reply@example.com
|
||||
from: bounce+%(task_uuid)s@example.com
|
||||
subject: "Openstack Email Notification"
|
||||
template: null
|
||||
token:
|
||||
email_current_user: false
|
||||
reply: no-reply@example.com
|
||||
from: bounce+%(task_uuid)s@example.com
|
||||
subject: "Openstack Email Notification"
|
||||
template: null
|
||||
completed:
|
||||
email_current_user: false
|
||||
reply: no-reply@example.com
|
||||
from: bounce+%(task_uuid)s@example.com
|
||||
subject: "Openstack Email Notification"
|
||||
template: null
|
||||
# A null template will cause the email not to send
|
||||
# Also emails to the given roles on the project
|
||||
# email_roles:
|
||||
# - project_admin
|
||||
# Or sends to an email set in the task cache
|
||||
# email_in_task_cache: true
|
||||
# Or sends to an arbitrary admin email
|
||||
# email_additional_addresses:
|
||||
# - admin@example.org
|
||||
|
||||
# These are cascading overrides for the default settings:
|
||||
TASK_SETTINGS:
|
||||
|
93
stacktask/actions/utils.py
Normal file
93
stacktask/actions/utils.py
Normal file
@ -0,0 +1,93 @@
|
||||
import six
|
||||
from smtplib import SMTPException
|
||||
|
||||
from stacktask.api.v1.utils import create_notification
|
||||
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.template import loader
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def send_email(to_addresses, context, conf, task):
|
||||
"""
|
||||
Function for sending emails from actions
|
||||
"""
|
||||
|
||||
if not conf.get('template'):
|
||||
return
|
||||
|
||||
if not to_addresses:
|
||||
return
|
||||
if isinstance(to_addresses, six.string_types):
|
||||
to_addresses = [to_addresses]
|
||||
elif isinstance(to_addresses, set):
|
||||
to_addresses = list(to_addresses)
|
||||
|
||||
text_template = loader.get_template(
|
||||
conf['template'],
|
||||
using='include_etc_templates')
|
||||
|
||||
html_template = conf.get('html_template', None)
|
||||
if html_template:
|
||||
html_template = loader.get_template(
|
||||
html_template,
|
||||
using='include_etc_templates')
|
||||
|
||||
try:
|
||||
message = text_template.render(context)
|
||||
# from_email is the return-path and is distinct from the
|
||||
# message headers
|
||||
from_email = conf.get('from')
|
||||
if not from_email:
|
||||
from_email = conf['reply']
|
||||
elif "%(task_uuid)s" in from_email:
|
||||
from_email = from_email % {'task_uuid': task.uuid}
|
||||
|
||||
reply_email = conf['reply']
|
||||
# these are the message headers which will be visible to
|
||||
# the email client.
|
||||
headers = {
|
||||
'X-StackTask-Task-UUID': task.uuid,
|
||||
# From needs to be set to be distinct from return-path
|
||||
'From': reply_email,
|
||||
'Reply-To': reply_email,
|
||||
}
|
||||
|
||||
email = EmailMultiAlternatives(
|
||||
conf['subject'],
|
||||
message,
|
||||
from_email,
|
||||
to_addresses,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
if html_template:
|
||||
email.attach_alternative(
|
||||
html_template.render(context), "text/html")
|
||||
|
||||
email.send(fail_silently=False)
|
||||
return True
|
||||
|
||||
except SMTPException as e:
|
||||
notes = {
|
||||
'errors':
|
||||
("Error: '%s' while sending additional email for task: %s"
|
||||
% (e, task.uuid))
|
||||
}
|
||||
|
||||
errors_conf = settings.TASK_SETTINGS.get(
|
||||
task.task_type, settings.DEFAULT_TASK_SETTINGS).get(
|
||||
'errors', {}).get("SMTPException", {})
|
||||
|
||||
if errors_conf:
|
||||
notification = create_notification(
|
||||
task, notes, error=True,
|
||||
engines=errors_conf.get('engines', True))
|
||||
|
||||
if errors_conf.get('notification') == "acknowledge":
|
||||
notification.acknowledged = True
|
||||
notification.save()
|
||||
else:
|
||||
create_notification(task, notes, error=True)
|
||||
|
||||
return False
|
115
stacktask/actions/v1/misc.py
Normal file
115
stacktask/actions/v1/misc.py
Normal file
@ -0,0 +1,115 @@
|
||||
# Copyright (C) 2016 Catalyst IT Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from stacktask.actions.v1.base import BaseAction
|
||||
from stacktask.actions import user_store
|
||||
from stacktask.actions.utils import send_email
|
||||
|
||||
|
||||
class SendAdditionalEmailAction(BaseAction):
|
||||
|
||||
def set_email(self, conf):
|
||||
self.emails = set()
|
||||
if conf.get('email_current_user'):
|
||||
self.add_note("Adding the current user's email address")
|
||||
if settings.USERNAME_IS_EMAIL:
|
||||
self.emails.add(self.action.task.keystone_user['username'])
|
||||
else:
|
||||
try:
|
||||
self.emails.add(self.action.task.keystone_user['email'])
|
||||
except KeyError:
|
||||
self.add_note("Could not add current user email address")
|
||||
|
||||
if conf.get('email_roles'):
|
||||
roles = set(conf.get('email_roles'))
|
||||
project_id = self.action.task.keystone_user['project_id']
|
||||
self.add_note('Adding email addresses for roles %s in project %s'
|
||||
% (roles, project_id))
|
||||
|
||||
id_manager = user_store.IdentityManager()
|
||||
users = id_manager.list_users(project_id)
|
||||
for user in users:
|
||||
user_roles = [role.name for role in user.roles]
|
||||
if roles.intersection(user_roles):
|
||||
if settings.USERNAME_IS_EMAIL:
|
||||
self.emails.add(user.name)
|
||||
else:
|
||||
self.emails.add(user.email)
|
||||
|
||||
if conf.get('email_task_cache'):
|
||||
task_emails = self.task.cache.get('additional_emails', [])
|
||||
if isinstance(task_emails, six.string_types):
|
||||
task_emails = [task_emails]
|
||||
for email in task_emails:
|
||||
self.emails.add(email)
|
||||
|
||||
for email in conf.get('email_additional_addresses', []):
|
||||
self.emails.add(email)
|
||||
|
||||
def _validate(self):
|
||||
self.action.valid = True
|
||||
self.action.save()
|
||||
|
||||
def _pre_approve(self):
|
||||
self.perform_action('initial')
|
||||
|
||||
def _post_approve(self):
|
||||
self.perform_action('token')
|
||||
|
||||
def _submit(self, data):
|
||||
self.perform_action('completed')
|
||||
|
||||
def perform_action(self, stage):
|
||||
self._validate()
|
||||
|
||||
task = self.action.task
|
||||
for action in task.actions:
|
||||
if not action.valid:
|
||||
return
|
||||
|
||||
email_conf = self.settings.get(stage, {})
|
||||
|
||||
# If either of these are false we won't be sending anything.
|
||||
if not email_conf or not email_conf.get('template'):
|
||||
return
|
||||
|
||||
self.set_email(email_conf)
|
||||
|
||||
if not self.emails:
|
||||
self.add_note(self.emails)
|
||||
self.add_note("Email address not set. Stage: %s" % stage)
|
||||
return
|
||||
|
||||
self.add_note("Sending emails to: %s" % self.emails)
|
||||
|
||||
actions = {}
|
||||
for action in task.actions:
|
||||
act = action.get_action()
|
||||
actions[str(act)] = act
|
||||
|
||||
context = {
|
||||
'task': task,
|
||||
'actions': actions
|
||||
}
|
||||
|
||||
result = send_email(self.emails, context, email_conf, task)
|
||||
|
||||
if not result:
|
||||
self.add_note("Unable to send additional email. Stage: %s" % stage)
|
||||
else:
|
||||
self.add_note("Additional email sent. Stage: %s" % stage)
|
@ -22,6 +22,7 @@ from stacktask.actions.v1.users import (
|
||||
from stacktask.actions.v1.resources import (
|
||||
NewDefaultNetworkAction, NewProjectDefaultNetworkAction,
|
||||
SetProjectQuotaAction)
|
||||
from stacktask.actions.v1.misc import SendAdditionalEmailAction
|
||||
|
||||
|
||||
# Update settings dict with tuples in the format:
|
||||
@ -52,3 +53,7 @@ register_action_class(
|
||||
serializers.NewProjectDefaultNetworkSerializer)
|
||||
register_action_class(
|
||||
SetProjectQuotaAction, serializers.SetProjectQuotaSerializer)
|
||||
|
||||
# Register Misc actions:
|
||||
register_action_class(
|
||||
SendAdditionalEmailAction, serializers.SendAdditionalEmailSerializer)
|
||||
|
@ -87,3 +87,7 @@ class AddDefaultUsersToProjectSerializer(serializers.Serializer):
|
||||
|
||||
class SetProjectQuotaSerializer(serializers.Serializer):
|
||||
pass
|
||||
|
||||
|
||||
class SendAdditionalEmailSerializer(serializers.Serializer):
|
||||
pass
|
||||
|
@ -19,7 +19,7 @@ from django.utils import timezone
|
||||
from stacktask.api import utils
|
||||
from stacktask.api.v1.views import APIViewWithLogger
|
||||
from stacktask.api.v1.utils import (
|
||||
send_email, create_notification, create_token, create_task_hash,
|
||||
send_stage_email, create_notification, create_token, create_task_hash,
|
||||
add_task_id_for_roles)
|
||||
from stacktask.exceptions import SerializerMissingException
|
||||
|
||||
@ -213,7 +213,7 @@ class TaskView(APIViewWithLogger):
|
||||
|
||||
# send initial confirmation email:
|
||||
email_conf = class_conf.get('emails', {}).get('initial', None)
|
||||
send_email(task, email_conf)
|
||||
send_stage_email(task, email_conf)
|
||||
|
||||
action_models = task.actions
|
||||
approve_list = [act.get_action().auto_approve for act in action_models]
|
||||
@ -249,7 +249,7 @@ class TaskView(APIViewWithLogger):
|
||||
# will throw a key error if the token template has not
|
||||
# been specified
|
||||
email_conf = class_conf['emails']['token']
|
||||
send_email(task, email_conf, token)
|
||||
send_stage_email(task, email_conf, token)
|
||||
return {'notes': ['created token']}, 200
|
||||
except KeyError as e:
|
||||
import traceback
|
||||
@ -361,7 +361,7 @@ class TaskView(APIViewWithLogger):
|
||||
self.task_type, settings.DEFAULT_TASK_SETTINGS)
|
||||
email_conf = class_conf.get(
|
||||
'emails', {}).get('completed', None)
|
||||
send_email(task, email_conf)
|
||||
send_stage_email(task, email_conf)
|
||||
return {'notes': ["Task completed successfully."]}, 200
|
||||
|
||||
|
||||
|
8
stacktask/api/v1/templates/email_update_started.txt
Normal file
8
stacktask/api/v1/templates/email_update_started.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Hello,
|
||||
|
||||
We have had an email address change request from you. We have sent a confirmation email to your new email: {{ actions.UpdateUserEmailAction.new_email }}
|
||||
|
||||
If this was not you please get in touch with an administrator immediately.
|
||||
|
||||
Kind Regards,
|
||||
The Openstack Team
|
@ -292,6 +292,7 @@ class modify_dict_settings(override_settings):
|
||||
|
||||
Available operations:
|
||||
Standard operations:
|
||||
- 'update': A dict on dict operation to update final dict with value.
|
||||
- 'override': Either overrides or adds the value to the dictionary.
|
||||
- 'delete': Removes the value from the dictionary.
|
||||
|
||||
@ -365,6 +366,8 @@ class modify_dict_settings(override_settings):
|
||||
holding_dict[final_key] = operation['value']
|
||||
elif op_type == "delete":
|
||||
del holding_dict[final_key]
|
||||
elif op_type == "update":
|
||||
holding_dict[final_key].update(operation['value'])
|
||||
else:
|
||||
val = holding_dict.get(final_key, [])
|
||||
items = operation['value']
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2015 Catalyst IT Ltd
|
||||
# Copyright (C) 2015 Catalyst IT Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
@ -14,16 +14,16 @@
|
||||
|
||||
import mock
|
||||
|
||||
from django.test.utils import override_settings
|
||||
from django.core import mail
|
||||
|
||||
from rest_framework import status
|
||||
|
||||
from stacktask.api.models import Task, Token
|
||||
from stacktask.api.v1.tests import (FakeManager, setup_temp_cache,
|
||||
StacktaskAPITestCase)
|
||||
StacktaskAPITestCase, modify_dict_settings)
|
||||
from stacktask.api.v1 import tests
|
||||
|
||||
from django.core import mail
|
||||
from django.test.utils import override_settings
|
||||
|
||||
|
||||
@mock.patch('stacktask.actions.user_store.IdentityManager',
|
||||
FakeManager)
|
||||
@ -752,26 +752,227 @@ class TaskViewTests(StacktaskAPITestCase):
|
||||
response = self.client.post(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
data = {'email': "new_test@example.com", 'username': "new",
|
||||
'project_name': 'new_project'}
|
||||
response = self.client.post(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, {'notes': ['task created']})
|
||||
|
||||
new_task = Task.objects.all()[0]
|
||||
url = "/v1/tasks/" + new_task.uuid
|
||||
|
||||
headers = {
|
||||
'project_name': "test_project",
|
||||
'project_id': "test_project_id",
|
||||
'roles': "admin,_member_",
|
||||
'roles': "admin",
|
||||
'username': "test",
|
||||
'user_id': "test_user_id",
|
||||
'email': "test@example.com",
|
||||
'authenticated': True
|
||||
}
|
||||
response = self.client.post(url, {'approved': True}, format='json',
|
||||
headers=headers)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
new_token = Token.objects.all()[0]
|
||||
url = "/v1/tokens/" + new_token.token
|
||||
|
||||
data = {'confirm': True, 'password': '1234'}
|
||||
response = self.client.post(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
@modify_dict_settings(
|
||||
TASK_SETTINGS=[
|
||||
{'key_list': ['invite_user', 'additional_actions'],
|
||||
'operation': 'append',
|
||||
'value': ['SendAdditionalEmailAction']},
|
||||
{'key_list': ['invite_user', 'action_settings',
|
||||
'SendAdditionalEmailAction', 'initial'],
|
||||
'operation': 'update',
|
||||
'value': {
|
||||
'subject': 'email_update_additional',
|
||||
'template': 'email_update_started.txt',
|
||||
'email_roles': ['project_admin'],
|
||||
'email_current_user': False,
|
||||
}
|
||||
}
|
||||
])
|
||||
def test_additional_emails_roles(self):
|
||||
"""
|
||||
Tests the sending of additional emails to a set of roles in a project
|
||||
"""
|
||||
|
||||
# NOTE(amelia): sending this email here is probably not the intended
|
||||
# case. It would be more useful in cases such as a quota update or a
|
||||
# child project being created that all the project admins should be
|
||||
# notified of
|
||||
|
||||
project = mock.Mock()
|
||||
project.id = 'test_project_id'
|
||||
project.name = 'test_project'
|
||||
project.domain = 'default'
|
||||
|
||||
user = mock.Mock()
|
||||
user.id = 'test_user_id'
|
||||
user.name = "test@example.com"
|
||||
user.email = "test@example.com"
|
||||
user.domain = 'default'
|
||||
|
||||
user2 = mock.Mock()
|
||||
user2.id = 'test_user_id_2'
|
||||
user2.name = "test2@example.com"
|
||||
user2.email = "test2@example.com"
|
||||
user2.domain = 'default'
|
||||
|
||||
user3 = mock.Mock()
|
||||
user3.id = 'test_user_id_3'
|
||||
user3.name = "test3@example.com"
|
||||
user3.email = "test3@example.com"
|
||||
user3.domain = 'default'
|
||||
|
||||
project.roles = {user.id: ['project_admin', '_member_'],
|
||||
user2.id: ['project_admin', '_member_'],
|
||||
user3.id: ['project_mod', '_member_']}
|
||||
|
||||
setup_temp_cache({'test_project': project},
|
||||
{user.id: user, user2.id: user2, user3.id: user3})
|
||||
|
||||
url = "/v1/actions/InviteUser"
|
||||
headers = {
|
||||
'project_name': "test_project",
|
||||
'project_id': "test_project_id",
|
||||
'roles': "project_admin,_member_,project_mod",
|
||||
'username': "test@example.com",
|
||||
'user_id': "test_user_id",
|
||||
'authenticated': True
|
||||
}
|
||||
new_task = Task.objects.all()[0]
|
||||
url = "/v1/tasks/" + new_task.uuid
|
||||
response = self.client.post(url, {'approved': True}, format='json',
|
||||
headers=headers)
|
||||
|
||||
data = {'email': "new_test@example.com",
|
||||
'roles': ['_member_']}
|
||||
response = self.client.post(url, data, format='json', headers=headers)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
response.data,
|
||||
{'notes': ['created token']}
|
||||
)
|
||||
self.assertEqual(response.data, {'notes': ['created token']})
|
||||
|
||||
self.assertEqual(len(mail.outbox), 2)
|
||||
|
||||
self.assertEqual(len(mail.outbox[0].to), 2)
|
||||
self.assertEqual(set(mail.outbox[0].to),
|
||||
set([user.email, user2.email]))
|
||||
self.assertEqual(mail.outbox[0].subject, 'email_update_additional')
|
||||
|
||||
# Test that the token email gets sent to the other addresses
|
||||
self.assertEqual(mail.outbox[1].to[0], 'new_test@example.com')
|
||||
|
||||
new_token = Token.objects.all()[0]
|
||||
url = "/v1/tokens/" + new_token.token
|
||||
|
||||
data = {'confirm': True, 'password': '1234'}
|
||||
response = self.client.post(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
@modify_dict_settings(
|
||||
TASK_SETTINGS=[
|
||||
{'key_list': ['invite_user', 'additional_actions'],
|
||||
'operation': 'override',
|
||||
'value': ['SendAdditionalEmailAction']},
|
||||
{'key_list': ['invite_user', 'action_settings',
|
||||
'SendAdditionalEmailAction', 'initial'],
|
||||
'operation': 'update',
|
||||
'value':{
|
||||
'subject': 'invite_user_additional',
|
||||
'template': 'email_update_started.txt',
|
||||
'email_additional_addresses': ['admin@example.com'],
|
||||
'email_current_user': False,
|
||||
}
|
||||
}
|
||||
])
|
||||
def test_email_additional_addresses(self):
|
||||
"""
|
||||
Tests the sending of additional emails an admin email set in
|
||||
the conf
|
||||
"""
|
||||
|
||||
project = mock.Mock()
|
||||
project.id = 'test_project_id'
|
||||
project.name = 'test_project'
|
||||
project.domain = 'default'
|
||||
|
||||
user = mock.Mock()
|
||||
user.id = 'test_user_id'
|
||||
user.name = "test@example.com"
|
||||
user.email = "test@example.com"
|
||||
user.domain = 'default'
|
||||
|
||||
project.roles = {user.id: ['project_admin', '_member_']}
|
||||
setup_temp_cache({'test_project': project}, {user.id: user, })
|
||||
|
||||
url = "/v1/actions/InviteUser"
|
||||
headers = {
|
||||
'project_name': "test_project",
|
||||
'project_id': "test_project_id",
|
||||
'roles': "project_admin,_member_,project_mod",
|
||||
'username': "test@example.com",
|
||||
'user_id': "test_user_id",
|
||||
'authenticated': True
|
||||
}
|
||||
|
||||
data = {'email': "new_test@example.com", 'roles': ['_member_']}
|
||||
response = self.client.post(url, data, format='json', headers=headers)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, {'notes': ['created token']})
|
||||
|
||||
self.assertEqual(len(mail.outbox), 2)
|
||||
|
||||
self.assertEqual(set(mail.outbox[0].to),
|
||||
set(['admin@example.com']))
|
||||
self.assertEqual(mail.outbox[0].subject, 'invite_user_additional')
|
||||
|
||||
# Test that the token email gets sent to the other addresses
|
||||
self.assertEqual(mail.outbox[1].to[0], 'new_test@example.com')
|
||||
|
||||
new_token = Token.objects.all()[0]
|
||||
url = "/v1/tokens/" + new_token.token
|
||||
data = {'password': 'testpassword'}
|
||||
response = self.client.post(url, data, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
@modify_dict_settings(
|
||||
TASK_SETTINGS=[
|
||||
{'key_list': ['invite_user', 'additional_actions'],
|
||||
'operation': 'override',
|
||||
'value': ['SendAdditionalEmailAction']},
|
||||
{'key_list': ['invite_user', 'action_settings',
|
||||
'SendAdditionalEmailAction', 'initial'],
|
||||
'operation': 'update',
|
||||
'value':{
|
||||
'subject': 'invite_user_additional',
|
||||
'template': 'email_update_started.txt',
|
||||
'email_additional_addresses': ['admin@example.com'],
|
||||
'email_current_user': False,
|
||||
}
|
||||
}
|
||||
])
|
||||
def test_email_additional_action_invalid(self):
|
||||
"""
|
||||
The additional email actions should not send an email if the
|
||||
action is invalid.
|
||||
"""
|
||||
|
||||
setup_temp_cache({}, {})
|
||||
|
||||
url = "/v1/actions/InviteUser"
|
||||
headers = {
|
||||
'project_name': "test_project",
|
||||
'project_id': "test_project_id",
|
||||
'roles': "project_admin,_member_,project_mod",
|
||||
'username': "test@example.com",
|
||||
'user_id': "test_user_id",
|
||||
'authenticated': True
|
||||
}
|
||||
data = {'email': "test@example.com", 'roles': ["_member_"],
|
||||
'project_id': 'test_project_id'}
|
||||
response = self.client.post(url, data, format='json', headers=headers)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(response.data, {'errors': ['actions invalid']})
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
|
@ -44,7 +44,7 @@ def create_token(task):
|
||||
return token
|
||||
|
||||
|
||||
def send_email(task, email_conf, token=None):
|
||||
def send_stage_email(task, email_conf, token=None):
|
||||
if not email_conf:
|
||||
return
|
||||
|
||||
|
@ -25,7 +25,7 @@ from rest_framework.views import APIView
|
||||
from stacktask.api import utils
|
||||
from stacktask.api.models import Notification, Task, Token
|
||||
from stacktask.api.v1.utils import (
|
||||
create_notification, create_token, parse_filters, send_email)
|
||||
create_notification, create_token, parse_filters, send_stage_email)
|
||||
|
||||
|
||||
class APIViewWithLogger(APIView):
|
||||
@ -437,7 +437,7 @@ class TaskDetail(APIViewWithLogger):
|
||||
# will throw a key error if the token template has not
|
||||
# been specified
|
||||
email_conf = class_conf['emails']['token']
|
||||
send_email(task, email_conf, token)
|
||||
send_stage_email(task, email_conf, token)
|
||||
return Response({'notes': ['created token']},
|
||||
status=200)
|
||||
except KeyError as e:
|
||||
@ -493,7 +493,7 @@ class TaskDetail(APIViewWithLogger):
|
||||
task.task_type, settings.DEFAULT_TASK_SETTINGS)
|
||||
email_conf = class_conf.get(
|
||||
'emails', {}).get('completed', None)
|
||||
send_email(task, email_conf)
|
||||
send_stage_email(task, email_conf)
|
||||
|
||||
return Response(
|
||||
{'notes': ["Task completed successfully."]},
|
||||
@ -610,7 +610,7 @@ class TokenList(APIViewWithLogger):
|
||||
# will throw a key error if the token template has not
|
||||
# been specified
|
||||
email_conf = class_conf['emails']['token']
|
||||
send_email(task, email_conf, token)
|
||||
send_stage_email(task, email_conf, token)
|
||||
except KeyError as e:
|
||||
notes = {
|
||||
'errors': [
|
||||
@ -786,7 +786,7 @@ class TokenDetail(APIViewWithLogger):
|
||||
token.task.task_type, settings.DEFAULT_TASK_SETTINGS)
|
||||
email_conf = class_conf.get(
|
||||
'emails', {}).get('completed', None)
|
||||
send_email(token.task, email_conf)
|
||||
send_stage_email(token.task, email_conf)
|
||||
|
||||
return Response(
|
||||
{'notes': ["Token submitted successfully."]},
|
||||
|
@ -152,6 +152,20 @@ DEFAULT_ACTION_SETTINGS = {
|
||||
}
|
||||
},
|
||||
},
|
||||
'SendAdditionalEmailAction': {
|
||||
'initial': {
|
||||
'reply': 'no-reply@example.com',
|
||||
'from': 'bounce+%(task_uuid)s@example.com'
|
||||
},
|
||||
'token': {
|
||||
'reply': 'no-reply@example.com',
|
||||
'from': 'bounce+%(task_uuid)s@example.com'
|
||||
},
|
||||
'completed': {
|
||||
'reply': 'no-reply@example.com',
|
||||
'from': 'bounce+%(task_uuid)s@example.com'
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
TASK_SETTINGS = {
|
||||
|
@ -20,6 +20,7 @@ def dict_merge(a, b):
|
||||
Recursively merges two dicts.
|
||||
If both a and b have a key who's value is a dict then dict_merge is called
|
||||
on both values and the result stored in the returned dictionary.
|
||||
B is the override.
|
||||
"""
|
||||
if not isinstance(b, dict):
|
||||
return b
|
||||
|
Loading…
x
Reference in New Issue
Block a user