Skip to content

Commit

Permalink
Allow having multiple contacts with same role for one component
Browse files Browse the repository at this point in the history
Add a field count_limit to ContactRole to define the limit of
this role for each component.
The value default is 1, and define 0 as unlimited. When exceed
the limit, a ValidationError exception will be raised.

JIRA: PDC-1056
  • Loading branch information
ycheng-aa committed Oct 19, 2015
1 parent 9f6c492 commit 181b5bf
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 12 deletions.
216 changes: 211 additions & 5 deletions pdc/apps/component/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2637,6 +2637,108 @@ def test_patch_global_component_contacts(self):
self.assertEqual(response.data['contact'], data['contact'])
self.assertNumChanges([1])

def test_create_global_component_contacts_exceed_count_limit(self):
data = {'component': 'python', 'role': 'cc', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'cc', 'contact': {'mail_name': 'maillist1'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_create_global_component_contacts_for_unlimited_role(self):
data = {'component': 'python', 'role': 'watcher', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'watcher', 'contact': {'mail_name': 'maillist1'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'watcher',
'contact': {"username": "person1", "email": "person1@test.com"}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'watcher',
'contact': {"username": "person2", "email": "person2@test.com"}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_create_global_component_contacts_for_role_limit_3(self):
data = {'component': 'python', 'role': 'allow_3_role', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'allow_3_role', 'contact': {'mail_name': 'maillist1'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'allow_3_role',
'contact': {"username": "person1", "email": "person1@test.com"}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'allow_3_role',
'contact': {"username": "person2", "email": "person2@test.com"}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_patch_global_component_contact_exceed_count_limit(self):
data = {'component': 'python', 'role': 'cc', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
data = {'component': 'python', 'role': 'watcher', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
pk = response.data['id']
url = reverse('globalcomponentcontacts-detail', args=[pk])
data = {'role': 'cc'}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_patch_global_component_contacts_for_role_limit_3(self):
data = {'component': 'python', 'role': 'allow_3_role', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'allow_3_role', 'contact': {'mail_name': 'maillist1'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'cc',
'contact': {"username": "person1", "email": "person1@test.com"}}
response = self.client.post(self.list_url, data, format='json')
pk_cc = response.data['id']
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': 'python', 'role': 'watcher',
'contact': {"username": "person2", "email": "person2@test.com"}}
response = self.client.post(self.list_url, data, format='json')
pk_watcher = response.data['id']

self.assertEqual(response.status_code, status.HTTP_201_CREATED)

# fourth for allow_3_role
url = reverse('globalcomponentcontacts-detail', args=[pk_cc])
data = {'role': 'allow_3_role'}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)

url = reverse('globalcomponentcontacts-detail', args=[pk_watcher])
data = {'role': 'allow_3_role'}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

# remove one and patch again
response = self.client.delete(reverse('globalcomponentcontacts-detail', args=[pk_cc]))
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

url = reverse('globalcomponentcontacts-detail', args=[pk_watcher])
data = {'role': 'allow_3_role'}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)


class ReleaseComponentContactInfoRESTTestCase(TestCaseWithChangeSetMixin, APITestCase):
fixtures = [
Expand Down Expand Up @@ -2668,11 +2770,13 @@ def test_list_release_component_contacts(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 2)
results = response.data.get('results')
self.assertEqual(results[0]['role'], 'pm')
self.assertEqual(results[0]['contact']['email'], 'person1@test.com')

self.assertEqual(results[1]['role'], 'qe_ack')
self.assertEqual(results[1]['contact']['mail_name'], 'maillist2')
for result in results:
if result['id'] == 1:
self.assertEqual(result['role'], 'pm')
self.assertEqual(result['contact']['email'], 'person1@test.com')
else:
self.assertEqual(result['role'], 'qe_ack')
self.assertEqual(result['contact']['mail_name'], 'maillist2')

def test_retrieve_release_component_contacts(self):
response = self.client.get(reverse('releasecomponentcontacts-detail', args=[2]))
Expand Down Expand Up @@ -2743,3 +2847,105 @@ def test_patch_release_component_contacts(self):
response.data['contact'].pop('id')
self.assertEqual(response.data['contact'], data['contact'])
self.assertNumChanges([1])

def test_create_release_component_contacts_exceed_count_limit(self):
data = {'component': {'id': 2}, 'role': 'cc', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'cc', 'contact': {'mail_name': 'maillist1'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_create_release_component_contacts_for_unlimited_role(self):
data = {'component': {'id': 2}, 'role': 'watcher', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'watcher', 'contact': {'mail_name': 'maillist1'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'watcher',
'contact': {"username": "person1", "email": "person1@test.com"}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'watcher',
'contact': {"username": "person2", "email": "person2@test.com"}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_create_release_component_contacts_for_role_limit_3(self):
data = {'component': {'id': 2}, 'role': 'allow_3_role', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'allow_3_role', 'contact': {'mail_name': 'maillist1'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'allow_3_role',
'contact': {"username": "person1", "email": "person1@test.com"}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'allow_3_role',
'contact': {"username": "person2", "email": "person2@test.com"}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_patch_release_component_contact_exceed_count_limit(self):
data = {'component': {'id': 2}, 'role': 'cc', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
data = {'component': {'id': 2}, 'role': 'watcher', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
pk = response.data['id']
url = reverse('releasecomponentcontacts-detail', args=[pk])
data = {'role': 'cc'}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_patch_release_component_contacts_for_role_limit_3(self):
data = {'component': {'id': 2}, 'role': 'allow_3_role', 'contact': {'mail_name': 'maillist2'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'allow_3_role', 'contact': {'mail_name': 'maillist1'}}
response = self.client.post(self.list_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'cc',
'contact': {"username": "person1", "email": "person1@test.com"}}
response = self.client.post(self.list_url, data, format='json')
pk_cc = response.data['id']
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

data = {'component': {'id': 2}, 'role': 'watcher',
'contact': {"username": "person2", "email": "person2@test.com"}}
response = self.client.post(self.list_url, data, format='json')
pk_watcher = response.data['id']

self.assertEqual(response.status_code, status.HTTP_201_CREATED)

# fourth for allow_3_role
url = reverse('releasecomponentcontacts-detail', args=[pk_cc])
data = {'role': 'allow_3_role'}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)

url = reverse('releasecomponentcontacts-detail', args=[pk_watcher])
data = {'role': 'allow_3_role'}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

# remove one and patch again
response = self.client.delete(reverse('releasecomponentcontacts-detail', args=[pk_cc]))
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

url = reverse('releasecomponentcontacts-detail', args=[pk_watcher])
data = {'role': 'allow_3_role'}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
16 changes: 16 additions & 0 deletions pdc/apps/contact/fixtures/tests/contact_role.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,21 @@
"fields": {
"name": "cc"
}
},
{
"model": "contact.ContactRole",
"pk": 4,
"fields": {
"name": "watcher",
"count_limit": 0
}
},
{
"model": "contact.ContactRole",
"pk": 5,
"fields": {
"name": "allow_3_role",
"count_limit": 3
}
}
]
27 changes: 27 additions & 0 deletions pdc/apps/contact/migrations/0004_auto_20151016_1441.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('contact', '0003_auto_20151001_1309'),
]

operations = [
migrations.AddField(
model_name='contactrole',
name='count_limit',
field=models.IntegerField(default=1, help_text='Contact count limit of the role for each component.'),
),
migrations.AlterUniqueTogether(
name='globalcomponentcontact',
unique_together=set([('role', 'component', 'contact')]),
),
migrations.AlterUniqueTogether(
name='releasecomponentcontact',
unique_together=set([('role', 'component', 'contact')]),
),
]
29 changes: 23 additions & 6 deletions pdc/apps/contact/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
from django.db import models
from django.db.models.query import QuerySet
from django.forms.models import model_to_dict
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _


class ContactRole(models.Model):

name = models.CharField(max_length=128, unique=True)
count_limit = models.IntegerField(default=1,
help_text=_('Contact count limit of the role for each component.'))
UNLIMITED = 0

def __unicode__(self):
return u"%s" % self.name
Expand Down Expand Up @@ -142,18 +147,30 @@ def create(self, **kwargs):
return self.get_queryset().create(**create_kwargs)


class GlobalComponentContact(models.Model):
class ValidateRoleCountMixin(object):

def clean(self):
if self.role.count_limit != ContactRole.UNLIMITED:
q = type(self).objects.filter(component=self.component, role=self.role)
if self.pk:
q = q.exclude(pk=self.pk)
if q.count() >= self.role.count_limit:
raise ValidationError(
{'detail': 'Exceed contact role limit for the component. The limit is %d.' % self.role.count_limit})


class GlobalComponentContact(ValidateRoleCountMixin, models.Model):

role = models.ForeignKey(ContactRole, on_delete=models.PROTECT)
contact = models.ForeignKey(Contact, on_delete=models.PROTECT)
component = models.ForeignKey('component.GlobalComponent',
on_delete=models.PROTECT)

def __unicode__(self):
return u'%s: %s: %s' % (unicode(self.component), self.contact_role, unicode(self.contact))
return u'%s: %s: %s' % (unicode(self.component), unicode(self.role), unicode(self.contact))

class Meta:
unique_together = (('role', 'component'),)
unique_together = (('role', 'component', 'contact'), )

def export(self, fields=None):
return {
Expand All @@ -163,18 +180,18 @@ def export(self, fields=None):
}


class ReleaseComponentContact(models.Model):
class ReleaseComponentContact(ValidateRoleCountMixin, models.Model):

role = models.ForeignKey(ContactRole, on_delete=models.PROTECT)
contact = models.ForeignKey(Contact, on_delete=models.PROTECT)
component = models.ForeignKey('component.ReleaseComponent',
on_delete=models.PROTECT)

def __unicode__(self):
return u'%s: %s: %s' % (unicode(self.component), self.contact_role, unicode(self.contact))
return u'%s: %s: %s' % (unicode(self.component), unicode(self.role), unicode(self.contact))

class Meta:
unique_together = (('role', 'component'), )
unique_together = (('role', 'component', 'contact'), )

def export(self, fields=None):
return {
Expand Down
6 changes: 5 additions & 1 deletion pdc/apps/contact/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ class ContactRoleSerializer(StrictSerializerMixin,
serializers.HyperlinkedModelSerializer):
name = serializers.SlugField()

def to_representation(self, instance):
count_limit = instance.count_limit if instance.count_limit != ContactRole.UNLIMITED else 'unlimited'
return {'name': instance.name, 'count_limit': count_limit}

class Meta:
model = ContactRole
fields = ('name', )
fields = ('name', 'count_limit')


class PersonSerializer(DynamicFieldsSerializerMixin,
Expand Down

0 comments on commit 181b5bf

Please sign in to comment.