mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-22 18:56:15 +02:00
Unit tests, get domains for invited member
This commit is contained in:
parent
c5de4b3a1d
commit
3d6417bb99
3 changed files with 328 additions and 62 deletions
|
@ -2043,11 +2043,11 @@ class MembersTable extends LoadTableBase {
|
||||||
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_MEMBERS)) {
|
if (member_permissions.includes(UserPortfolioPermissionChoices.EDIT_MEMBERS)) {
|
||||||
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Members:</strong> Can manage members including inviting new members, removing current members, and assigning domains to members.";
|
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Members:</strong> Can manage members including inviting new members, removing current members, and assigning domains to members.";
|
||||||
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MEMBERS)) {
|
} else if (member_permissions.includes(UserPortfolioPermissionChoices.VIEW_MEMBERS)) {
|
||||||
permissionsHTML += "<p> class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Members (view-only):</strong> Can view all organizational members. Can't manage any members.";
|
permissionsHTML += "<p class='margin-top-1 p--blockquote'><strong class='text-base-dark'>Members (view-only):</strong> Can view all organizational members. Can't manage any members.";
|
||||||
}
|
}
|
||||||
// if there are no additional permissions, display a no additional permissions message
|
// if there are no additional permissions, display a no additional permissions message
|
||||||
if (!permissionsHTML) {
|
if (!permissionsHTML) {
|
||||||
permissionsHTML += "<p><b>No additional permissions:</b> There are no additional permissions for this member.</p>";
|
permissionsHTML += "<p class='margin-top-1 p--blockquote'><b>No additional permissions:</b> There are no additional permissions for this member.</p>";
|
||||||
}
|
}
|
||||||
// add permissions header in all cases
|
// add permissions header in all cases
|
||||||
permissionsHTML = "<div class='desktop:grid-col-7'><h4 class='margin-y-0 text-primary'>Additional permissions for this member</h4>" + permissionsHTML + "</div>";
|
permissionsHTML = "<div class='desktop:grid-col-7'><h4 class='margin-y-0 text-primary'>Additional permissions for this member</h4>" + permissionsHTML + "</div>";
|
||||||
|
@ -2058,7 +2058,7 @@ class MembersTable extends LoadTableBase {
|
||||||
showMoreButton = `
|
showMoreButton = `
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="usa-button--show-more-button usa-button usa-button--unstyled display-block margin-top-2"
|
class="usa-button--show-more-button usa-button usa-button--unstyled display-block margin-top-1"
|
||||||
data-for=${member_id}
|
data-for=${member_id}
|
||||||
>
|
>
|
||||||
<span>Expand</span>
|
<span>Expand</span>
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from registrar.models.domain import Domain
|
||||||
|
from registrar.models.domain_information import DomainInformation
|
||||||
|
from registrar.models.domain_invitation import DomainInvitation
|
||||||
from registrar.models.portfolio import Portfolio
|
from registrar.models.portfolio import Portfolio
|
||||||
from registrar.models.portfolio_invitation import PortfolioInvitation
|
from registrar.models.portfolio_invitation import PortfolioInvitation
|
||||||
from registrar.models.user import User
|
from registrar.models.user import User
|
||||||
|
from registrar.models.user_domain_role import UserDomainRole
|
||||||
from registrar.models.user_portfolio_permission import UserPortfolioPermission
|
from registrar.models.user_portfolio_permission import UserPortfolioPermission
|
||||||
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
||||||
from .test_views import TestWithUser
|
from registrar.tests.common import MockEppLib, create_test_user
|
||||||
from django_webtest import WebTest # type: ignore
|
from django_webtest import WebTest # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class GetPortfolioMembersJsonTest(TestWithUser, WebTest):
|
class GetPortfolioMembersJsonTest(MockEppLib, WebTest):
|
||||||
@classmethod
|
def setUp(self):
|
||||||
def setUpClass(cls):
|
super().setUp()
|
||||||
super().setUpClass()
|
self.user = create_test_user()
|
||||||
|
|
||||||
# Create additional users
|
# Create additional users
|
||||||
cls.user2 = User.objects.create(
|
self.user2 = User.objects.create(
|
||||||
username="test_user2",
|
username="test_user2",
|
||||||
first_name="Second",
|
first_name="Second",
|
||||||
last_name="User",
|
last_name="User",
|
||||||
|
@ -23,7 +27,7 @@ class GetPortfolioMembersJsonTest(TestWithUser, WebTest):
|
||||||
phone="8003112345",
|
phone="8003112345",
|
||||||
title="Member",
|
title="Member",
|
||||||
)
|
)
|
||||||
cls.user3 = User.objects.create(
|
self.user3 = User.objects.create(
|
||||||
username="test_user3",
|
username="test_user3",
|
||||||
first_name="Third",
|
first_name="Third",
|
||||||
last_name="User",
|
last_name="User",
|
||||||
|
@ -31,7 +35,7 @@ class GetPortfolioMembersJsonTest(TestWithUser, WebTest):
|
||||||
phone="8003113456",
|
phone="8003113456",
|
||||||
title="Member",
|
title="Member",
|
||||||
)
|
)
|
||||||
cls.user4 = User.objects.create(
|
self.user4 = User.objects.create(
|
||||||
username="test_user4",
|
username="test_user4",
|
||||||
first_name="Fourth",
|
first_name="Fourth",
|
||||||
last_name="User",
|
last_name="User",
|
||||||
|
@ -39,60 +43,66 @@ class GetPortfolioMembersJsonTest(TestWithUser, WebTest):
|
||||||
phone="8003114567",
|
phone="8003114567",
|
||||||
title="Admin",
|
title="Admin",
|
||||||
)
|
)
|
||||||
cls.email5 = "fifth@example.com"
|
self.user5 = User.objects.create(
|
||||||
|
username="test_user5",
|
||||||
|
first_name="Fifth",
|
||||||
|
last_name="User",
|
||||||
|
email="fifth@example.com",
|
||||||
|
phone="8003114568",
|
||||||
|
title="Admin",
|
||||||
|
)
|
||||||
|
self.email6 = "fifth@example.com"
|
||||||
|
|
||||||
# Create Portfolio
|
# Create Portfolio
|
||||||
cls.portfolio = Portfolio.objects.create(creator=cls.user, organization_name="Test Portfolio")
|
self.portfolio = Portfolio.objects.create(creator=self.user, organization_name="Test Portfolio")
|
||||||
|
|
||||||
# Assign permissions
|
# Assign permissions
|
||||||
UserPortfolioPermission.objects.create(
|
|
||||||
user=cls.user,
|
|
||||||
portfolio=cls.portfolio,
|
|
||||||
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
||||||
additional_permissions=[
|
|
||||||
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
|
||||||
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
UserPortfolioPermission.objects.create(
|
|
||||||
user=cls.user2,
|
|
||||||
portfolio=cls.portfolio,
|
|
||||||
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
|
||||||
)
|
|
||||||
UserPortfolioPermission.objects.create(
|
|
||||||
user=cls.user3,
|
|
||||||
portfolio=cls.portfolio,
|
|
||||||
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
|
||||||
)
|
|
||||||
UserPortfolioPermission.objects.create(
|
|
||||||
user=cls.user4,
|
|
||||||
portfolio=cls.portfolio,
|
|
||||||
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
||||||
)
|
|
||||||
PortfolioInvitation.objects.create(
|
|
||||||
email=cls.email5,
|
|
||||||
portfolio=cls.portfolio,
|
|
||||||
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
||||||
additional_permissions=[
|
|
||||||
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
|
||||||
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
self.app.set_user(self.user.username)
|
||||||
def tearDownClass(cls):
|
|
||||||
|
def tearDown(self):
|
||||||
|
UserDomainRole.objects.all().delete()
|
||||||
|
DomainInformation.objects.all().delete()
|
||||||
|
Domain.objects.all().delete()
|
||||||
PortfolioInvitation.objects.all().delete()
|
PortfolioInvitation.objects.all().delete()
|
||||||
UserPortfolioPermission.objects.all().delete()
|
UserPortfolioPermission.objects.all().delete()
|
||||||
Portfolio.objects.all().delete()
|
Portfolio.objects.all().delete()
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
super().tearDownClass()
|
super().tearDown()
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super().setUp()
|
|
||||||
self.app.set_user(self.user.username)
|
|
||||||
|
|
||||||
def test_get_portfolio_members_json_authenticated(self):
|
def test_get_portfolio_members_json_authenticated(self):
|
||||||
"""Test that portfolio members are returned properly for an authenticated user."""
|
"""Test that portfolio members are returned properly for an authenticated user."""
|
||||||
|
"""Also tests that reposnse is 200 when no domains"""
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
additional_permissions=[
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user2,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user3,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user4,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user5,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
)
|
||||||
|
|
||||||
response = self.app.get(reverse("get_portfolio_members_json"), params={"portfolio": self.portfolio.id})
|
response = self.app.get(reverse("get_portfolio_members_json"), params={"portfolio": self.portfolio.id})
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
data = response.json
|
data = response.json
|
||||||
|
@ -115,15 +125,207 @@ class GetPortfolioMembersJsonTest(TestWithUser, WebTest):
|
||||||
self.user3.email,
|
self.user3.email,
|
||||||
self.user4.email,
|
self.user4.email,
|
||||||
self.user4.email,
|
self.user4.email,
|
||||||
self.email5,
|
self.user5.email,
|
||||||
}
|
}
|
||||||
actual_emails = {member["email"] for member in data["members"]}
|
actual_emails = {member["email"] for member in data["members"]}
|
||||||
self.assertEqual(expected_emails, actual_emails)
|
self.assertEqual(expected_emails, actual_emails)
|
||||||
|
|
||||||
|
expected_roles = {
|
||||||
|
UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
|
UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
||||||
|
}
|
||||||
|
# Convert each member's roles list to a frozenset
|
||||||
|
actual_roles = {role for member in data["members"] for role in member["roles"]}
|
||||||
|
self.assertEqual(expected_roles, actual_roles)
|
||||||
|
|
||||||
|
expected_additional_permissions = {
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
}
|
||||||
|
actual_additional_permissions = {permission for member in data["members"] for permission in member["permissions"]}
|
||||||
|
self.assertTrue(expected_additional_permissions.issubset(actual_additional_permissions))
|
||||||
|
|
||||||
|
def test_get_portfolio_invited_json_authenticated(self):
|
||||||
|
"""Test that portfolio invitees are returned properly for an authenticated user."""
|
||||||
|
"""Also tests that reposnse is 200 when no domains"""
|
||||||
|
PortfolioInvitation.objects.create(
|
||||||
|
email=self.email6,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
additional_permissions=[
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.app.get(reverse("get_portfolio_members_json"), params={"portfolio": self.portfolio.id})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
data = response.json
|
||||||
|
|
||||||
|
# Check pagination info
|
||||||
|
self.assertEqual(data["page"], 1)
|
||||||
|
self.assertEqual(data["num_pages"], 1)
|
||||||
|
self.assertEqual(data["total"], 1)
|
||||||
|
self.assertEqual(data["unfiltered_total"], 1)
|
||||||
|
|
||||||
|
# Check the number of members
|
||||||
|
self.assertEqual(len(data["members"]), 1)
|
||||||
|
|
||||||
|
# Check member fields
|
||||||
|
expected_emails = {
|
||||||
|
self.email6
|
||||||
|
}
|
||||||
|
actual_emails = {member["email"] for member in data["members"]}
|
||||||
|
self.assertEqual(expected_emails, actual_emails)
|
||||||
|
|
||||||
|
expected_roles = {
|
||||||
|
UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
|
||||||
|
}
|
||||||
|
# Convert each member's roles list to a frozenset
|
||||||
|
actual_roles = {role for member in data["members"] for role in member["roles"]}
|
||||||
|
self.assertEqual(expected_roles, actual_roles)
|
||||||
|
|
||||||
|
expected_additional_permissions = {
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
}
|
||||||
|
actual_additional_permissions = {permission for member in data["members"] for permission in member["permissions"]}
|
||||||
|
self.assertTrue(expected_additional_permissions.issubset(actual_additional_permissions))
|
||||||
|
|
||||||
|
def test_get_portfolio_members_json_with_domains(self):
|
||||||
|
"""Test that portfolio members are returned properly for an authenticated user and the response includes
|
||||||
|
the domains that the member manages.."""
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
additional_permissions=[
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user2,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user3,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user4,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
)
|
||||||
|
|
||||||
|
domain = Domain.objects.create(
|
||||||
|
name="somedomain1.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
DomainInformation.objects.create(
|
||||||
|
creator=self.user,
|
||||||
|
domain=domain,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
)
|
||||||
|
|
||||||
|
UserDomainRole.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
domain=domain,
|
||||||
|
role=UserDomainRole.Roles.MANAGER,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.app.get(reverse("get_portfolio_members_json"), params={"portfolio": self.portfolio.id})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
data = response.json
|
||||||
|
|
||||||
|
# Check if the domain appears in the response JSON
|
||||||
|
domain_names = [
|
||||||
|
domain_name
|
||||||
|
for member in data["members"]
|
||||||
|
for domain_name in member.get("domain_names", [])
|
||||||
|
]
|
||||||
|
self.assertIn("somedomain1.com", domain_names)
|
||||||
|
|
||||||
|
def test_get_portfolio_invited_json_with_domains(self):
|
||||||
|
"""Test that portfolio invited members are returned properly for an authenticated user and the response includes
|
||||||
|
the domains that the member manages.."""
|
||||||
|
PortfolioInvitation.objects.create(
|
||||||
|
email=self.email6,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
additional_permissions=[
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
domain = Domain.objects.create(
|
||||||
|
name="somedomain1.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
DomainInformation.objects.create(
|
||||||
|
creator=self.user,
|
||||||
|
domain=domain,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
)
|
||||||
|
|
||||||
|
DomainInvitation.objects.create(
|
||||||
|
email=self.email6,
|
||||||
|
domain=domain,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.app.get(reverse("get_portfolio_members_json"), params={"portfolio": self.portfolio.id})
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
data = response.json
|
||||||
|
|
||||||
|
# Check if the domain appears in the response JSON
|
||||||
|
domain_names = [
|
||||||
|
domain_name
|
||||||
|
for member in data["members"]
|
||||||
|
for domain_name in member.get("domain_names", [])
|
||||||
|
]
|
||||||
|
self.assertIn("somedomain1.com", domain_names)
|
||||||
|
|
||||||
def test_pagination(self):
|
def test_pagination(self):
|
||||||
"""Test that pagination works properly when there are more members than page size."""
|
"""Test that pagination works properly when there are more members than page size."""
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
additional_permissions=[
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user2,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user3,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user4,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
)
|
||||||
|
PortfolioInvitation.objects.create(
|
||||||
|
email=self.email6,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
additional_permissions=[
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
# Create additional members to exceed page size of 10
|
# Create additional members to exceed page size of 10
|
||||||
for i in range(5, 15):
|
for i in range(6, 16):
|
||||||
user, _ = User.objects.get_or_create(
|
user, _ = User.objects.get_or_create(
|
||||||
username=f"test_user{i}",
|
username=f"test_user{i}",
|
||||||
first_name=f"User{i}",
|
first_name=f"User{i}",
|
||||||
|
@ -172,6 +374,40 @@ class GetPortfolioMembersJsonTest(TestWithUser, WebTest):
|
||||||
|
|
||||||
def test_search(self):
|
def test_search(self):
|
||||||
"""Test search functionality for portfolio members."""
|
"""Test search functionality for portfolio members."""
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
additional_permissions=[
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user2,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user3,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
)
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=self.user4,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
)
|
||||||
|
PortfolioInvitation.objects.create(
|
||||||
|
email=self.email6,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
||||||
|
additional_permissions=[
|
||||||
|
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||||
|
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
# Search by name
|
# Search by name
|
||||||
response = self.app.get(
|
response = self.app.get(
|
||||||
reverse("get_portfolio_members_json"), params={"portfolio": self.portfolio.id, "search_term": "Second"}
|
reverse("get_portfolio_members_json"), params={"portfolio": self.portfolio.id, "search_term": "Second"}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db.models import Value, F, CharField, TextField, Q, Case, When
|
from django.db.models import Value, F, CharField, TextField, Q, Case, When, OuterRef, Subquery
|
||||||
from django.db.models.functions import Concat, Coalesce
|
from django.db.models.functions import Concat, Coalesce
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.contrib.postgres.aggregates import ArrayAgg
|
from django.contrib.postgres.aggregates import ArrayAgg
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.db.models.functions import Cast
|
from django.db.models.functions import Cast
|
||||||
|
|
||||||
|
from registrar.models.domain_invitation import DomainInvitation
|
||||||
from registrar.models.portfolio_invitation import PortfolioInvitation
|
from registrar.models.portfolio_invitation import PortfolioInvitation
|
||||||
from registrar.models.user_domain_role import UserDomainRole
|
from registrar.models.user_domain_role import UserDomainRole
|
||||||
from registrar.models.user_portfolio_permission import UserPortfolioPermission
|
from registrar.models.user_portfolio_permission import UserPortfolioPermission
|
||||||
|
@ -89,7 +90,8 @@ def initial_permissions_search(portfolio):
|
||||||
# specify the output_field to ensure union has same column types
|
# specify the output_field to ensure union has same column types
|
||||||
output_field=CharField()
|
output_field=CharField()
|
||||||
),
|
),
|
||||||
distinct=True
|
distinct=True,
|
||||||
|
filter=Q(user__permissions__domain__isnull=False)
|
||||||
),
|
),
|
||||||
source=Value("permission", output_field=CharField()),
|
source=Value("permission", output_field=CharField()),
|
||||||
)
|
)
|
||||||
|
@ -110,7 +112,22 @@ def initial_permissions_search(portfolio):
|
||||||
|
|
||||||
|
|
||||||
def initial_invitations_search(portfolio):
|
def initial_invitations_search(portfolio):
|
||||||
"""Perform initial invitations search before applying any filters."""
|
"""Perform initial invitations search and get related DomainInvitation data based on the email."""
|
||||||
|
|
||||||
|
|
||||||
|
# Get DomainInvitation query for matching email
|
||||||
|
domain_invitations = DomainInvitation.objects.filter(
|
||||||
|
email=OuterRef('email'),
|
||||||
|
domain__isnull=False
|
||||||
|
).annotate(
|
||||||
|
domain_info=Concat(
|
||||||
|
F('domain__id'),
|
||||||
|
Value(':'),
|
||||||
|
F('domain__name'),
|
||||||
|
output_field=CharField()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
invitations = PortfolioInvitation.objects.filter(portfolio=portfolio)
|
invitations = PortfolioInvitation.objects.filter(portfolio=portfolio)
|
||||||
invitations = invitations.annotate(
|
invitations = invitations.annotate(
|
||||||
first_name=Value(None, output_field=CharField()),
|
first_name=Value(None, output_field=CharField()),
|
||||||
|
@ -118,7 +135,14 @@ def initial_invitations_search(portfolio):
|
||||||
email_display=F("email"),
|
email_display=F("email"),
|
||||||
last_active=Value("Invited", output_field=TextField()),
|
last_active=Value("Invited", output_field=TextField()),
|
||||||
member_display=F("email"),
|
member_display=F("email"),
|
||||||
domain_info=Value([], output_field=ArrayField(TextField())),
|
# ArrayAgg for multiple domain_invitations matched by email, filtered to exclude nulls
|
||||||
|
domain_info=Coalesce( # Use Coalesce to return an empty list if no domain invitations exist
|
||||||
|
ArrayAgg(
|
||||||
|
Subquery(domain_invitations.values('domain_info')),
|
||||||
|
distinct=True,
|
||||||
|
),
|
||||||
|
Value([], output_field=ArrayField(CharField())) # Ensure we return an empty list
|
||||||
|
),
|
||||||
source=Value("invitation", output_field=CharField()),
|
source=Value("invitation", output_field=CharField()),
|
||||||
).values(
|
).values(
|
||||||
"id",
|
"id",
|
||||||
|
@ -181,9 +205,15 @@ def serialize_members(request, portfolio, item, user):
|
||||||
"member_display": item.get("member_display", ""),
|
"member_display": item.get("member_display", ""),
|
||||||
"roles": (item.get("roles") or []),
|
"roles": (item.get("roles") or []),
|
||||||
"permissions": UserPortfolioPermission.get_portfolio_permissions(item.get("roles"), item.get("additional_permissions")),
|
"permissions": UserPortfolioPermission.get_portfolio_permissions(item.get("roles"), item.get("additional_permissions")),
|
||||||
# split domain_info array values into ids to form urls, and names
|
"domain_names": [
|
||||||
"domain_urls": [reverse("domain", kwargs={"pk": domain_info.split(":")[0]}) for domain_info in item.get("domain_info")],
|
domain_info.split(":")[1] for domain_info in (item.get("domain_info") or [])
|
||||||
"domain_names": [domain_info.split(":")[1] for domain_info in item.get("domain_info")],
|
if domain_info is not None # Prevent splitting None
|
||||||
|
],
|
||||||
|
"domain_urls": [
|
||||||
|
reverse("domain", kwargs={"pk": domain_info.split(":")[0]})
|
||||||
|
for domain_info in (item.get("domain_info") or [])
|
||||||
|
if domain_info is not None # Prevent splitting None
|
||||||
|
],
|
||||||
"is_admin": is_admin,
|
"is_admin": is_admin,
|
||||||
"last_active": item.get("last_active", ""),
|
"last_active": item.get("last_active", ""),
|
||||||
"action_url": action_url,
|
"action_url": action_url,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue