Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨(backend) allow to filter member on team access endpoint #200

Merged
merged 1 commit into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/backend/core/api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,23 @@ def get_queryset(self):
queryset = queryset.filter(team=self.kwargs["team_id"])

if self.action in {"list", "retrieve"}:
if query := self.request.GET.get("q", ""):
similarity = Max(
TrigramSimilarity(
Coalesce(Func("user__email", function="unaccent"), Value("")),
Func(Value(query), function="unaccent"),
)
+ TrigramSimilarity(
Coalesce(Func("user__name", function="unaccent"), Value("")),
Func(Value(query), function="unaccent"),
)
)
queryset = (
queryset.annotate(similarity=similarity)
.filter(similarity__gte=SIMILARITY_THRESHOLD)
.order_by("-similarity")
)

# Determine which role the logged-in user has in the team
user_role_query = models.TeamAccess.objects.filter(
user=self.request.user, team=self.kwargs["team_id"]
Expand Down
148 changes: 148 additions & 0 deletions src/backend/core/tests/team_accesses/test_api_team_accesses_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,154 @@ def test_api_team_accesses_list_authenticated_related():
)


def test_api_team_accesses_list_find_members(django_assert_num_queries):
"""
Authenticated users should be able to search users access with a case-insensitive and
partial query on the name.
"""
dave = factories.UserFactory(name="dave bowman", email=None)
frank = factories.UserFactory(name="frank poole", email=None)
mary = factories.UserFactory(name="mary poole", email=None)
nicole = factories.UserFactory(name="nicole foole", email=None)
factories.UserFactory(email="tester@ministry.fr", name="john doe")
factories.UserFactory(name="heywood floyd", email=None)
factories.UserFactory(name="Andrei Smyslov", email=None)
factories.TeamFactory.create_batch(10)

# Add Mary, Nicole and Dave in the same team
team = factories.TeamFactory(
name="Odyssey",
users=[
(mary, models.RoleChoices.OWNER),
(nicole, models.RoleChoices.ADMIN),
(dave, models.RoleChoices.MEMBER),
],
)
factories.TeamFactory(users=[(frank, models.RoleChoices.MEMBER)])

# Search users in the team Odyssey
client = APIClient()
client.force_login(mary)

# 5 queries are needed here:
# - 1 query: select on user authenticated
# - 4 queries: get all users, owner included (2 more queries)
with django_assert_num_queries(5):
response = client.get(
f"/api/v1.0/teams/{team.id!s}/accesses/",
)
assert response.status_code == HTTP_200_OK
assert response.json()["count"] == 3

# We can find David Bowman
# 3 queries are needed here:
# - 1 query: user authenticated
# - 2 queries: search user query with match
with django_assert_num_queries(3):
response = client.get(
f"/api/v1.0/teams/{team.id!s}/accesses/?q=bowman",
)
assert response.status_code == HTTP_200_OK
assert response.json()["count"] == 1
dave_access = dave.accesses.get(team=team)
assert response.json()["results"][0]["id"] == str(dave_access.id)

# We can find Nicole
# 3 queries are needed here:
# - 1 query: user authenticated
# - 2 queries: search user query with match
with django_assert_num_queries(3):
response = client.get(
f"/api/v1.0/teams/{team.id!s}/accesses/?q=nicol",
)
assert response.status_code == HTTP_200_OK
assert response.json()["count"] == 1
nicole_access = nicole.accesses.get(team=team)
assert response.json()["results"][0]["id"] == str(nicole_access.id)

# We can find Nicole and Mary
# 5 queries are needed here:
# - 1 query: select on user authenticated
# - 4 queries: search user query with match and the owner found (2 more queries)
with django_assert_num_queries(5):
response = client.get(
f"/api/v1.0/teams/{team.id!s}/accesses/?q=ool",
)
assert response.status_code == HTTP_200_OK
assert response.json()["count"] == 2
user_access_ids = sorted([user["id"] for user in response.json()["results"]])
mary_access = mary.accesses.get(team=team)
assert sorted([str(mary_access.id), str(nicole_access.id)]) == user_access_ids

# We cannot find Andrei, because he isn't a member of the current team
# 2 queries are needed here:
# - 1 query: select on user authenticated
# - 1 query: search user query with no match
with django_assert_num_queries(2):
response = client.get(
f"/api/v1.0/teams/{team.id!s}/accesses/?q=andrei",
)
assert response.status_code == HTTP_200_OK
assert response.json()["count"] == 0

# We can find Mary
# 5 queries are needed here:
# - 1 query: select on user authenticated
# - 4 queries: search user query with match and an owner found (2 more queries)
with django_assert_num_queries(5):
response = client.get(
f"/api/v1.0/teams/{team.id!s}/accesses/?q=mary",
)
assert response.status_code == HTTP_200_OK
assert response.json()["count"] == 1
assert response.json()["results"][0]["id"] == str(mary_access.id)


def test_api_team_accesses__list_find_members_by_email():
"""
Authenticated users should be able to search users access with a case-insensitive and
partial query on the email.
"""
user = factories.UserFactory(name=None)

# set all names to None to match only on emails
colleague1 = factories.UserFactory(name=None, email="prudence_crandall@edu.us")
colleague2 = factories.UserFactory(name=None, email="reinebrunehaut@gouv.fr")
colleague3 = factories.UserFactory(name=None, email="artemisia.gentileschi@arte.it")

# Add all colleague in the same team
team = factories.TeamFactory(
users=[
(user, models.RoleChoices.ADMIN),
(colleague1, models.RoleChoices.OWNER),
(colleague2, models.RoleChoices.ADMIN),
(colleague3, models.RoleChoices.MEMBER),
],
)
factories.TeamAccessFactory.create_batch(4)

# Create another team with similar email
factories.TeamFactory(
users=[
(
factories.UserFactory(name=None, email="bruneau@gouv.fr"),
models.RoleChoices.ADMIN,
),
],
)

# Search users in the team Odyssey
client = APIClient()
client.force_login(user)

response = client.get(
f"/api/v1.0/teams/{team.id!s}/accesses/?q=BRUNE",
)
assert response.status_code == HTTP_200_OK
assert response.json()["count"] == 1
assert response.json()["results"][0]["user"]["email"] == "reinebrunehaut@gouv.fr"


def test_api_team_accesses_list_authenticated_constant_numqueries(
django_assert_num_queries,
):
Expand Down
Loading