Skip to content

Commit

Permalink
Merge pull request #239 from Duke-GCB/email-template-endpoints
Browse files Browse the repository at this point in the history
Adds email templates API v2 endpoints
  • Loading branch information
johnbradley authored Jun 26, 2020
2 parents 19796c1 + 51844bb commit d495458
Show file tree
Hide file tree
Showing 10 changed files with 315 additions and 5 deletions.
1 change: 1 addition & 0 deletions d4s2_api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class DDSDeliveryAdmin(SimpleHistoryAdmin):
admin.site.register(DDSDeliveryError)
admin.site.register(DDSDeliveryShareUser)
admin.site.register(EmailTemplateSet)
admin.site.register(EmailTemplateType)
admin.site.register(UserEmailTemplateSet)
admin.site.register(S3Endpoint)
admin.site.register(S3User)
Expand Down
31 changes: 31 additions & 0 deletions d4s2_api/migrations/0039_auto_20200615_1338.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.28 on 2020-06-15 13:38
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('d4s2_api', '0038_auto_20200327_1857'),
]

operations = [
migrations.AddField(
model_name='emailtemplatetype',
name='help_text',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='emailtemplatetype',
name='sequence',
field=models.IntegerField(help_text='determines order', null=True),
),
migrations.AlterField(
model_name='emailtemplate',
name='template_set',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='email_templates', to='d4s2_api.EmailTemplateSet'),
),
]
20 changes: 20 additions & 0 deletions d4s2_api/migrations/0040_emailtemplateset_group_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.28 on 2020-06-17 15:45
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('d4s2_api', '0039_auto_20200615_1338'),
]

operations = [
migrations.AddField(
model_name='emailtemplateset',
name='group_name',
field=models.CharField(blank=True, help_text='group manager group assigned to this set', max_length=64),
),
]
47 changes: 46 additions & 1 deletion d4s2_api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

import uuid
from django.db import models
from django.db.models import Q
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, ValidationError
from django.contrib.auth.models import User, Group
from django.contrib.postgres.fields import JSONField
from simple_history.models import HistoricalRecords
from gcb_web_auth.utils import get_default_oauth_service, current_user_details, OAuthConfigurationException
from gcb_web_auth.models import DDSUserCredential, GroupManagerConnection
from gcb_web_auth.groupmanager import get_users_group_names


DEFAULT_EMAIL_TEMPLATE_SET_NAME = 'default'

Expand Down Expand Up @@ -84,6 +89,8 @@ class EmailTemplateSet(models.Model):
name = models.CharField(max_length=64, null=False, blank=False, unique=True)
cc_address = models.EmailField(blank=True, help_text='Optional address to CC when using this template set')
reply_address = models.EmailField(blank=True, help_text='Optional address to set as reply-to when using this template set (replacing the sending user\'s email address')
group_name = models.CharField(max_length=64, null=False, blank=True,
help_text='group manager group assigned to this set')

def __str__(self):
return self.name
Expand All @@ -95,6 +102,31 @@ def template_for_name(self, name):
raise EmailTemplateException(
"Setup Error: Unable to find email template for type {}".format(name))

@staticmethod
def get_group_names_for_user(user):
try:
user_details = current_user_details(get_default_oauth_service(), user)
duke_unique_id = user_details['dukeUniqueID']
group_manager_connection = GroupManagerConnection.objects.first()
if group_manager_connection and duke_unique_id:
group_manager_groups = get_users_group_names(group_manager_connection, duke_unique_id)
return [group_name for group_name in group_manager_groups if group_name]
else:
return []
except OAuthConfigurationException:
return []

@staticmethod
def get_for_user(user):
"""
Include a users default template set and those for the current user's groups.
"""
user_group_names = EmailTemplateSet.get_group_names_for_user(user)
return EmailTemplateSet.objects.filter(
Q(useremailtemplateset__user=user) |
Q(group_name__in=user_group_names)
)


class DeliveryBase(models.Model):
state = models.IntegerField(choices=State.DELIVERY_CHOICES, default=State.NEW, null=False)
Expand Down Expand Up @@ -238,6 +270,8 @@ class EmailTemplateType(models.Model):
Type of email template, e.g. share_project_viewer, delivery, final_notification
"""
name = models.CharField(max_length=64, null=False, blank=False, unique=True)
help_text = models.TextField(null=False, blank=True)
sequence = models.IntegerField(null=True, help_text='determines order')

def __str__(self):
return self.name
Expand All @@ -248,7 +282,7 @@ class EmailTemplate(models.Model):
Represents a base email message that can be sent
"""
history = HistoricalRecords()
template_set = models.ForeignKey(EmailTemplateSet, on_delete=models.CASCADE)
template_set = models.ForeignKey(EmailTemplateSet, on_delete=models.CASCADE, related_name='email_templates')
owner = models.ForeignKey(User, on_delete=models.CASCADE)
template_type = models.ForeignKey(EmailTemplateType, on_delete=models.CASCADE)
body = models.TextField(null=False, blank=False)
Expand All @@ -266,6 +300,17 @@ class Meta:
('template_set', 'template_type'),
)

@staticmethod
def get_for_user(user):
"""
Include a users default template set templates and those for the current user's groups.
"""
user_group_names = EmailTemplateSet.get_group_names_for_user(user)
return EmailTemplate.objects.filter(
Q(template_set__useremailtemplateset__user=user) |
Q(template_set__group_name__in=user_group_names)
)


class UserEmailTemplateSet(models.Model):
"""
Expand Down
6 changes: 5 additions & 1 deletion d4s2_api_v1/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
from rest_framework.exceptions import APIException, ValidationError
from rest_framework.decorators import detail_route
from rest_framework.response import Response
from d4s2_api.models import DDSDelivery, Share, State, UserEmailTemplateSet
from d4s2_api.models import DDSDelivery, Share, State, UserEmailTemplateSet, EmailTemplateSet
from d4s2_api_v1.serializers import DeliverySerializer, ShareSerializer
from switchboard.dds_util import DDSUtil, DDSMessageFactory
from django.core.urlresolvers import reverse
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Q
EMAIL_TEMPLATES_NOT_SETUP_MSG = """Email templates need to be setup for your account.
Please contact [email protected]."""
CANNOT_PASS_EMAIL_TEMPLATE_SET = """You cannot create this item by passing email_template_set,
Expand Down Expand Up @@ -46,6 +47,9 @@ def get_email_template_for_request(self):
:return: EmailTemplateSet
"""
try:
email_template_set_id = self.request.data.get('email_template_set_id')
if email_template_set_id:
return EmailTemplateSet.get_for_user(self.request.user).get(pk=email_template_set_id)
user_email_template_set = UserEmailTemplateSet.objects.get(user=self.request.user)
return user_email_template_set.email_template_set
except UserEmailTemplateSet.DoesNotExist:
Expand Down
12 changes: 12 additions & 0 deletions d4s2_api_v1/tests_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ def test_create(self, mock_response):
def test_get_email_template_for_request(self):
mixin = ModelWithEmailTemplateSetMixin()
mixin.request = Mock(user=self.user)
mixin.request.data = {}
email_template_set = mixin.get_email_template_for_request()
self.assertEqual(email_template_set, self.email_template_set)

Expand All @@ -439,6 +440,17 @@ def test_get_email_template_for_request(self):
mixin.get_email_template_for_request()
self.assertEqual(raised_exception.exception.detail[0], EMAIL_TEMPLATES_NOT_SETUP_MSG)

@patch('d4s2_api_v1.api.EmailTemplateSet')
def test_get_email_template_for_request_with_template_set_id(self, mock_email_template_set):
other_email_template_set = EmailTemplateSet.objects.create(name='otherset')
mixin = ModelWithEmailTemplateSetMixin()
mixin.request = Mock(user=self.user)
mixin.request.data = {'email_template_set_id': other_email_template_set.id}
email_template_set = mixin.get_email_template_for_request()
self.assertEqual(email_template_set, mock_email_template_set.get_for_user.return_value.get.return_value)
mock_email_template_set.get_for_user.assert_called_with(mixin.request.user)
mock_email_template_set.get_for_user.return_value.get.assert_called_with(pk=other_email_template_set.id)

def test_prevent_null_email_template_set(self):
mixin = ModelWithEmailTemplateSetMixin()
mixin.get_object = Mock()
Expand Down
27 changes: 25 additions & 2 deletions d4s2_api_v2/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
from d4s2_api_v2.serializers import DDSUserSerializer, DDSProjectSerializer, DDSProjectTransferSerializer, \
UserSerializer, S3EndpointSerializer, S3UserSerializer, S3BucketSerializer, S3DeliverySerializer, \
DDSProjectPermissionSerializer, DDSDeliveryPreviewSerializer, DDSAuthProviderSerializer, DDSAffiliateSerializer, \
AddUserSerializer, DDSProjectSummarySerializer
from d4s2_api.models import DDSDelivery, S3Endpoint, S3User, S3UserTypes, S3Bucket, S3Delivery
AddUserSerializer, DDSProjectSummarySerializer, EmailTemplateSetSerializer, EmailTemplateSerializer
from d4s2_api.models import DDSDelivery, S3Endpoint, S3User, S3UserTypes, S3Bucket, S3Delivery, EmailTemplateSet, \
EmailTemplate
from d4s2_api_v1.api import AlreadyNotifiedException, get_force_param, build_accept_url, DeliveryViewSet, \
ModelWithEmailTemplateSetMixin
from switchboard.s3_util import S3Exception, S3NoSuchBucket, SendDeliveryOperation
Expand Down Expand Up @@ -355,3 +356,25 @@ def get_or_register_user(self, request, pk):
dds_user = self._ds_operation(DDSUser.get_or_register_user, dds_util, auth_provider_id, pk)
serializer = DDSUserSerializer(dds_user)
return Response(serializer.data, status=status.HTTP_201_CREATED)


class EmailTemplateSetViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
return EmailTemplateSet.get_for_user(self.request.user)

permission_classes = (permissions.IsAuthenticated,)
serializer_class = EmailTemplateSetSerializer
queryset = EmailTemplateSet.objects.all()
filter_backends = (DjangoFilterBackend,)
filter_fields = ('name', )


class EmailTemplateViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
return EmailTemplate.get_for_user(self.request.user)

permission_classes = (permissions.IsAuthenticated,)
serializer_class = EmailTemplateSerializer
queryset = EmailTemplate.objects.all()
filter_backends = (DjangoFilterBackend,)
filter_fields = ('template_set', )
38 changes: 37 additions & 1 deletion d4s2_api_v2/serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from rest_framework import serializers
from django.contrib.auth.models import User
from switchboard.dds_util import DDSUtil
from d4s2_api.models import S3Endpoint, S3User, S3Bucket, S3Delivery, EmailTemplateSet, UserEmailTemplateSet
from d4s2_api.models import S3Endpoint, S3User, S3Bucket, S3Delivery, EmailTemplateSet, UserEmailTemplateSet, EmailTemplate
from d4s2_api_v2.models import DDSDeliveryPreview


Expand Down Expand Up @@ -192,5 +192,41 @@ class DDSAffiliateSerializer(serializers.Serializer):
class Meta:
resource_name = 'duke-ds-auth-provider-affiliates'


class AddUserSerializer(serializers.Serializer):
username = serializers.CharField()


class EmailTemplateSetSerializer(serializers.ModelSerializer):
email_templates = serializers.SerializerMethodField()
default = serializers.SerializerMethodField()

def get_email_templates(self, obj):
return [x.id for x in obj.email_templates.order_by('template_type__sequence')]

def get_default(self, obj):
return UserEmailTemplateSet.objects.filter(
email_template_set=obj,
user=self.context['request'].user
).exists()

class Meta:
model = EmailTemplateSet
resource_name = 'email-template-set'
fields = ('id', 'name', 'cc_address', 'reply_address', 'email_templates', 'default')


class EmailTemplateSerializer(serializers.ModelSerializer):
type = serializers.SerializerMethodField()
help_text = serializers.SerializerMethodField()

def get_type(self, obj):
return obj.template_type.name

def get_help_text(self, obj):
return obj.template_type.help_text

class Meta:
model = EmailTemplate
resource_name = 'email-template'
fields = ('id', 'template_set', 'owner', 'type', 'help_text', 'body', 'subject')
Loading

0 comments on commit d495458

Please sign in to comment.