feat: API to list role-holder addresses (#7291)

* feat: API to list role-holder addresses

* test: Test new API endpoint

* fix: role_holder_addresses gets own API token

* refactor: Move role_holder_addresses to ietf.api.views

* test: test for group.utils.role_holder_emails

* test: Clean up test_role_holder_addresses

* fix: Missed a change in urls.py

* refactor: Remove old view

* chore: Remove unused import

* chore: Remove unused import
This commit is contained in:
Jennifer Richards 2024-04-05 16:02:40 -03:00 committed by GitHub
parent 8d7e8d6830
commit 6b4a806e42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 111 additions and 3 deletions

View file

@ -991,6 +991,28 @@ class CustomApiTests(TestCase):
self.assertCountEqual(result.keys(), ["addresses"]) self.assertCountEqual(result.keys(), ["addresses"])
self.assertCountEqual(result["addresses"], Email.objects.filter(active=True).values_list("address", flat=True)) self.assertCountEqual(result["addresses"], Email.objects.filter(active=True).values_list("address", flat=True))
@override_settings(APP_API_TOKENS={"ietf.api.views.role_holder_addresses": ["valid-token"]})
def test_role_holder_addresses(self):
url = urlreverse("ietf.api.views.role_holder_addresses")
r = self.client.get(url, headers={})
self.assertEqual(r.status_code, 403, "No api token, no access")
r = self.client.get(url, headers={"X-Api-Key": "not-valid-token"})
self.assertEqual(r.status_code, 403, "Bad api token, no access")
r = self.client.post(url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 405, "Bad method, no access")
emails = EmailFactory.create_batch(5)
email_queryset = Email.objects.filter(pk__in=[e.pk for e in emails])
with mock.patch("ietf.api.views.role_holder_emails", return_value=email_queryset):
r = self.client.get(url, headers={"X-Api-Key": "valid-token"})
self.assertEqual(r.status_code, 200, "Good api token and method, access")
content_dict = json.loads(r.content)
self.assertCountEqual(content_dict.keys(), ["addresses"])
self.assertEqual(
content_dict["addresses"],
sorted(e.address for e in emails),
)
class DirectAuthApiTests(TestCase): class DirectAuthApiTests(TestCase):

View file

@ -28,6 +28,8 @@ urlpatterns = [
url(r'^export/personal-information/$', api_views.PersonalInformationExportView.as_view()), url(r'^export/personal-information/$', api_views.PersonalInformationExportView.as_view()),
# Email alias information for groups # Email alias information for groups
url(r'^group/group-aliases/$', api_views.group_aliases), url(r'^group/group-aliases/$', api_views.group_aliases),
# Email addresses belonging to role holders
url(r'^group/role-holder-addresses/$', api_views.role_holder_addresses),
# Let IESG members set positions programmatically # Let IESG members set positions programmatically
url(r'^iesg/position', views_ballot.api_set_position), url(r'^iesg/position', views_ballot.api_set_position),
# Let Meetecho set session video URLs # Let Meetecho set session video URLs

View file

@ -29,7 +29,7 @@ from ietf.api import _api_list
from ietf.api.ietf_utils import is_valid_token, requires_api_token from ietf.api.ietf_utils import is_valid_token, requires_api_token
from ietf.api.serializer import JsonExportMixin from ietf.api.serializer import JsonExportMixin
from ietf.doc.utils import DraftAliasGenerator, fuzzy_find_documents from ietf.doc.utils import DraftAliasGenerator, fuzzy_find_documents
from ietf.group.utils import GroupAliasGenerator from ietf.group.utils import GroupAliasGenerator, role_holder_emails
from ietf.ietfauth.utils import role_required from ietf.ietfauth.utils import role_required
from ietf.ietfauth.views import send_account_creation_email from ietf.ietfauth.views import send_account_creation_email
from ietf.meeting.models import Meeting from ietf.meeting.models import Meeting
@ -500,3 +500,18 @@ def active_email_list(request):
} }
) )
return HttpResponse(status=405) return HttpResponse(status=405)
@requires_api_token
def role_holder_addresses(request):
if request.method == "GET":
return JsonResponse(
{
"addresses": list(
role_holder_emails()
.order_by("address")
.values_list("address", flat=True)
)
}
)
return HttpResponse(status=405)

View file

@ -18,10 +18,16 @@ import debug # pyflakes:ignore
from ietf.doc.factories import DocumentFactory, WgDraftFactory, EditorialDraftFactory from ietf.doc.factories import DocumentFactory, WgDraftFactory, EditorialDraftFactory
from ietf.doc.models import DocEvent, RelatedDocument, Document from ietf.doc.models import DocEvent, RelatedDocument, Document
from ietf.group.models import Role, Group from ietf.group.models import Role, Group
from ietf.group.utils import get_group_role_emails, get_child_group_role_emails, get_group_ad_emails, GroupAliasGenerator from ietf.group.utils import (
get_group_role_emails,
get_child_group_role_emails,
get_group_ad_emails,
GroupAliasGenerator,
role_holder_emails,
)
from ietf.group.factories import GroupFactory, RoleFactory from ietf.group.factories import GroupFactory, RoleFactory
from ietf.person.factories import PersonFactory, EmailFactory from ietf.person.factories import PersonFactory, EmailFactory
from ietf.person.models import Person from ietf.person.models import Email, Person
from ietf.utils.test_utils import login_testing_unauthorized, TestCase from ietf.utils.test_utils import login_testing_unauthorized, TestCase
class StreamTests(TestCase): class StreamTests(TestCase):
@ -240,3 +246,41 @@ class GroupRoleEmailTests(TestCase):
self.assertGreater(len(emails), 0) self.assertGreater(len(emails), 0)
for item in emails: for item in emails:
self.assertIn('@', item) self.assertIn('@', item)
def test_role_holder_emails(self):
# The test fixtures create a bunch of addresses that pollute this test's results - disable them
Email.objects.update(active=False)
role_holders = [
RoleFactory(name_id="member", group__type_id=gt).person
for gt in [
"ag",
"area",
"dir",
"iab",
"ietf",
"irtf",
"nomcom",
"rg",
"team",
"wg",
"rag",
]
]
# Expect an additional active email to be included
EmailFactory(
person=role_holders[0],
active=True,
)
# Do not expect an inactive email to be included
EmailFactory(
person=role_holders[1],
active=False,
)
# Do not expect address on a role-holder for a different group type
RoleFactory(name_id="member", group__type_id="adhoc") # arbitrary type not in the of-interest list
self.assertCountEqual(
role_holder_emails(),
Email.objects.filter(active=True, person__in=role_holders),
)

View file

@ -425,3 +425,28 @@ class GroupAliasGenerator:
chair_emails = get_group_role_emails(group, ["chair", "delegate"]) chair_emails = get_group_role_emails(group, ["chair", "delegate"])
if chair_emails: if chair_emails:
yield group.acronym + "-chairs", ["ietf"], list(chair_emails) yield group.acronym + "-chairs", ["ietf"], list(chair_emails)
def role_holder_emails():
"""Get queryset of active Emails for group role holders"""
group_types_of_interest = [
"ag",
"area",
"dir",
"iab",
"ietf",
"irtf",
"nomcom",
"rg",
"team",
"wg",
"rag",
]
roles = Role.objects.filter(
group__state__slug="active",
group__type__in=group_types_of_interest,
)
emails = Email.objects.filter(active=True).exclude(
address__startswith="unknown-email-"
)
return emails.filter(person__role__in=roles).distinct()