Skip to content
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
4 changes: 3 additions & 1 deletion src/sentry/api/serializers/models/team.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ def get_attrs(

if self._expand("projects"):
project_teams = ProjectTeam.objects.get_for_teams_with_org_cache(item_list)
projects = [pt.project for pt in project_teams]

# A project can be on multiple teams, dedupe before serializing
projects = list({pt.project_id: pt.project for pt in project_teams}.values())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like this is the only user of get_for_teams_with_org_cache, we could in theory dedupe on the query level.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(letting the clanker respond)

We still need the full join table for the project_map loop below (line 265-267) which maps each team to its projects. If we deduped at the query level we'd lose that team-to-project mapping. Could split it into two queries but that seems worse.


projects_by_id = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could also do this deduping after projects_by_id to make it a little cleaner

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(letting the clanker respond)

The dedup has to happen before serialize() — that's where get_features_for_projects() iterates the list and appends to a defaultdict(list) keyed by project. If duplicates are in the input, features get appended N times to the same list before projects_by_id ever gets built.

project.id: data
Expand Down
14 changes: 14 additions & 0 deletions tests/sentry/api/serializers/test_team.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,20 @@ def test_simple(self) -> None:
"externalTeams": [],
}

def test_project_on_multiple_teams_no_duplicate_features(self) -> None:
"""A project belonging to multiple teams should not have its features list duplicated."""
user = self.create_user(username="foo")
organization = self.create_organization()
team1 = self.create_team(organization=organization)
team2 = self.create_team(organization=organization)
self.create_project(teams=[team1, team2], organization=organization)

result = serialize([team1, team2], user, TeamWithProjectsSerializer())
features = result[0]["projects"][0]["features"]

assert len(features) > 0
assert features == list(dict.fromkeys(features))


class TeamSCIMSerializerTest(TestCase):
def test_simple_with_members(self) -> None:
Expand Down
Loading