def test_team_actors_to_user_ids(self): team1 = self.create_team() team2 = self.create_team() team3 = self.create_team() # team with no active members users = [self.create_user() for i in range(0, 8)] self.create_member(user=users[0], organization=self.organization, teams=[team1]) self.create_member(user=users[1], organization=self.organization, teams=[team1]) self.create_member(user=users[2], organization=self.organization, teams=[team1]) self.create_member(user=users[3], organization=self.organization, teams=[team1, team2]) self.create_member(user=users[4], organization=self.organization, teams=[team2, self.team]) self.create_member(user=users[5], organization=self.organization, teams=[team2]) # Inactive member member6 = self.create_member( user=users[6], organization=self.organization, teams=[ team2, team3]) team_member6 = OrganizationMemberTeam.objects.filter(organizationmember_id=member6.id) for team_member in team_member6: team_member.update(is_active=False) # Member without teams self.create_member(user=users[7], organization=self.organization, teams=[]) team_actors = [Actor(team1.id, Team), Actor(team2.id, Team), Actor(team3.id, Team)] user_ids = [user.id for user in users] assert team_actors_to_user_ids(team_actors, user_ids) == { team1.id: set([users[0].id, users[1].id, users[2].id, users[3].id]), team2.id: set([users[3].id, users[4].id, users[5].id]), }
def resolve_actor(owner, project_id): """ Convert an Owner object into an Actor """ from sentry.api.fields.actor import Actor from sentry.models import User, Team if owner.type == 'user': try: user_id = User.objects.filter( email__iexact=owner.identifier, is_active=True, sentry_orgmember_set__organizationmemberteam__team__projectteam__project_id=project_id, ).values_list('id', flat=True)[0] except IndexError: raise UnknownActor return Actor(user_id, User) if owner.type == 'team': try: team_id = Team.objects.filter( projectteam__project_id=project_id, slug=owner.identifier, ).values_list('id', flat=True)[0] except IndexError: return UnknownActor return Actor(team_id, Team) raise TypeError('Unknown actor type: %r' % owner.type)
def test_basic(self): owners = [ Owner("user", self.user.email), Owner("team", self.team.slug) ] assert resolve_actors(owners, self.project.id) == { owners[0]: Actor(self.user.id, User), owners[1]: Actor(self.team.id, Team), }
def test_build_events_by_actor(self): events = self.team1_events + self.team2_events + self.user4_events events_by_actor = { Actor(self.team1.id, Team): set(self.team1_events), Actor(self.team2.id, Team): set(self.team2_events), Actor(self.user3.id, User): set(self.team1_events), Actor(self.user4.id, User): set(self.user4_events), } assert build_events_by_actor(self.project.id, events, self.user_ids) == events_by_actor
def test_users(self): actor = Actor(self.user.id, User) result = extract_user_ids_from_mentions(self.organization.id, [actor]) assert result['users'] == set([self.user.id]) assert result['team_users'] == set() other_user = self.create_user() result = extract_user_ids_from_mentions( self.organization.id, [actor, Actor(other_user.id, User)]) assert result['users'] == set([self.user.id, other_user.id]) assert result['team_users'] == set()
def test_convert_actors_to_user_set(self): user1 = self.create_user() user2 = self.create_user() user3 = self.create_user() user4 = self.create_user() team1 = self.create_team() team2 = self.create_team() self.create_member(user=user1, organization=self.organization, teams=[team1]) self.create_member(user=user2, organization=self.organization, teams=[team2]) self.create_member(user=user3, organization=self.organization, teams=[team1, team2]) self.create_member(user=user4, organization=self.organization, teams=[]) team1_events = set([ self.create_event(self.project.id), self.create_event(self.project.id), self.create_event(self.project.id), self.create_event(self.project.id), ]) team2_events = set([ self.create_event(self.project.id), self.create_event(self.project.id), self.create_event(self.project.id), self.create_event(self.project.id), ]) user4_events = set([ self.create_event(self.project.id), self.create_event(self.project.id) ]) events_by_actor = { Actor(team1.id, Team): team1_events, Actor(team2.id, Team): team2_events, Actor(user3.id, User): team1_events.union(team2_events), Actor(user4.id, User): user4_events, } user_by_events = { user1.id: team1_events, user2.id: team2_events, user3.id: team1_events.union(team2_events), user4.id: user4_events, } assert convert_actors_to_users(events_by_actor, user_by_events.keys()) == user_by_events
def resolve_actors(owners, project_id): """ Convert a list of Owner objects into a dictionary of {Owner: Actor} pairs. Actors not identified are returned as None. """ from sentry.api.fields.actor import Actor from sentry.models import User, Team if not owners: return {} users, teams = [], [] owners_lookup = {} for owner in owners: # teams aren't technical case insensitive, but teams also # aren't allowed to have non-lowercase in slugs, so # this kinda works itself out correctly since they won't match owners_lookup[(owner.type, owner.identifier.lower())] = owner if owner.type == 'user': users.append(owner) elif owner.type == 'team': teams.append(owner) actors = {} if users: actors.update({ ('user', email.lower()): Actor(u_id, User) for u_id, email in User.objects.filter( reduce( operator.or_, [Q(emails__email__iexact=o.identifier) for o in users] ), # We don't require verified emails # emails__is_verified=True, is_active=True, sentry_orgmember_set__organizationmemberteam__team__projectteam__project_id=project_id, ).distinct().values_list('id', 'emails__email') }) if teams: actors.update({ ('team', slug): Actor(t_id, Team) for t_id, slug in Team.objects.filter( slug__in=[o.identifier for o in teams], projectteam__project_id=project_id, ).values_list('id', 'slug') }) return { o: actors.get((o.type, o.identifier.lower())) for o in owners }
def test_teams(self): member_user = self.create_user() self.create_member(user=member_user, organization=self.organization, role="member", teams=[self.team]) actor = Actor(self.team.id, Team) result = extract_user_ids_from_mentions(self.organization.id, [actor]) assert result["users"] == set() assert result["team_users"] == set([self.user.id, member_user.id]) # Explicitly mentioned users shouldn't be included in team_users result = extract_user_ids_from_mentions( self.organization.id, [Actor(member_user.id, User), actor]) assert result["users"] == set([member_user.id]) assert result["team_users"] == set([self.user.id])
def test_get_owners_basic(self): matcher = Matcher('path', '*.py') ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([ Rule(matcher, [ Owner('user', self.user.email), Owner('team', self.team.slug), ]), ]), fallthrough=True, ) # No data matches assert ProjectOwnership.get_owners(self.project.id, {}) == (ProjectOwnership.Everyone, None) assert ProjectOwnership.get_owners(self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'foo.py', }] } }) == ([Actor(self.user.id, User), Actor(self.team.id, Team)], matcher) assert ProjectOwnership.get_owners(self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'xxxx', }] } }) == (ProjectOwnership.Everyone, None) # When fallthrough = False, we don't implicitly assign to Everyone ProjectOwnership.objects.filter( project_id=self.project.id, ).update(fallthrough=False) assert ProjectOwnership.get_owners(self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'xxxx', }] } }) == ([], None)
def test_team(self): data = { 'actor_field': "team:1", } serializer = DummySerializer(data=data) assert serializer.is_valid() assert serializer.object == {'actor_field': Actor(id=1, type=Team)}
def test_simple(self): data = { 'actor_field': "user:1", } serializer = DummySerializer(data=data) assert serializer.is_valid() assert serializer.object == {'actor_field': Actor(id=1, type=User)}
def test_legacy_user_fallback(self): data = { 'actor_field': "1", } serializer = DummySerializer(data=data) assert serializer.is_valid() assert serializer.object == {'actor_field': Actor(id=1, type=User)}
def test_get_owners_basic(self): rule_a = Rule(Matcher("path", "*.py"), [Owner("team", self.team.slug)]) rule_b = Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)]) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([rule_a, rule_b]), fallthrough=True ) # No data matches assert ProjectOwnership.get_owners(self.project.id, {}) == (ProjectOwnership.Everyone, None) # Match only rule_a self.assert_ownership_equals( ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "foo.py"}]}} ), ([Actor(self.team.id, Team)], [rule_a]), ) # Match only rule_b self.assert_ownership_equals( ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "src/thing.txt"}]}} ), ([Actor(self.user.id, User)], [rule_b]), ) # Matches both rule_a and rule_b self.assert_ownership_equals( ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "src/foo.py"}]}} ), ([Actor(self.user.id, User), Actor(self.team.id, Team)], [rule_a, rule_b]), ) assert ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "xxxx"}]}} ) == (ProjectOwnership.Everyone, None) # When fallthrough = False, we don't implicitly assign to Everyone ProjectOwnership.objects.filter(project_id=self.project.id).update(fallthrough=False) assert ProjectOwnership.get_owners( self.project.id, {"stacktrace": {"frames": [{"filename": "xxxx"}]}} ) == ([], None)
def resolve_actors(owners, project_id): """ Convert a list of Owner objects into a dictionary of {Owner: Actor} pairs. Actors not identified are returned as None. """ from sentry.api.fields.actor import Actor from sentry.ownership.grammar import Owner from sentry.models import User, Team if not owners: return {} users, teams = [], [] owners_to_actors = {o: None for o in owners} for owner in owners: if owner.type == 'user': users.append(owner) elif owner.type == 'team': teams.append(owner) actors = [] if users: actors.extend([( 'user', email, Actor(u_id, User) ) for u_id, email in User.objects.filter( reduce(operator.or_, [Q(email__iexact=o.identifier) for o in users]), is_active=True, sentry_orgmember_set__organizationmemberteam__team__projectteam__project_id =project_id, ).values_list('id', 'email')]) if teams: actors.extend([('team', slug, Actor(t_id, Team)) for t_id, slug in Team.objects.filter( slug__in=[o.identifier for o in teams], projectteam__project_id=project_id, ).values_list('id', 'slug')]) for type, identifier, actor in actors: owners_to_actors[Owner(type, identifier)] = actor return owners_to_actors
def build_events_by_actor(project_id, events, user_ids): """ build_events_by_actor(project_id: Int, events: Set(Events), user_ids: Set[Int]) -> Map[Actor, Set(Events)] """ events_by_actor = defaultdict(set) for event in events: # TODO(LB): I Know this is inefficient. # ProjectOwnership.get_owners is O(n) queries and I'm doing that O(len(events)) times # I will create a follow-up PR to address this method's efficiency problem # Just wanted to make as few changes as possible for now. actors, __ = ProjectOwnership.get_owners(project_id, event.data) if actors == ProjectOwnership.Everyone: actors = [Actor(user_id, User) for user_id in user_ids] for actor in actors: events_by_actor[actor].add(event) return events_by_actor
def test_teams(self): # Normal team owner1 = Owner('team', self.team.slug) actor1 = Actor(self.team.id, Team) # Team that doesn't exist owner2 = Owner('team', 'nope') actor2 = None # A team that's not ours otherteam = Team.objects.exclude(projectteam__project_id=self.project.id)[0] owner3 = Owner('team', otherteam.slug) actor3 = None assert resolve_actors([owner1, owner2, owner3], self.project.id) == { owner1: actor1, owner2: actor2, owner3: actor3, }
def test_users(self): # Normal user owner1 = Owner("user", self.user.email) actor1 = Actor(self.user.id, User) # An extra secondary email email1 = self.create_useremail(self.user, None, is_verified=True).email owner2 = Owner("user", email1) actor2 = actor1 # They map to the same user since it's just a secondary email # Another secondary email, that isn't verified email2 = self.create_useremail(self.user, None, is_verified=False).email owner3 = Owner("user", email2) # Intentionally allow unverified emails # actor3 = None actor3 = actor1 # An entirely unknown user owner4 = Owner("user", "nope") actor4 = None # A user that doesn't belong with us otheruser = self.create_user() owner5 = Owner("user", otheruser.email) actor5 = None # Case-insensitive for user owner6 = Owner("user", self.user.email.upper()) actor6 = actor1 assert resolve_actors([owner1, owner2, owner3, owner4, owner5, owner6], self.project.id) == { owner1: actor1, owner2: actor2, owner3: actor3, owner4: actor4, owner5: actor5, owner6: actor6, }
def test_get_owners_basic(self): rule_a = Rule( Matcher('path', '*.py'), [ Owner('team', self.team.slug), ]) rule_b = Rule( Matcher('path', 'src/*'), [ Owner('user', self.user.email), ]) ProjectOwnership.objects.create( project_id=self.project.id, schema=dump_schema([rule_a, rule_b]), fallthrough=True, ) # No data matches assert ProjectOwnership.get_owners(self.project.id, {}) == (ProjectOwnership.Everyone, None) # Match only rule_a self.assert_ownership_equals(ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'foo.py', }] } } ), ([Actor(self.team.id, Team)], [rule_a])) # Match only rule_b self.assert_ownership_equals(ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'src/thing.txt', }] } } ), ([Actor(self.user.id, User)], [rule_b])) # Matches both rule_a and rule_b self.assert_ownership_equals(ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'src/foo.py', }] } } ), ([Actor(self.user.id, User), Actor(self.team.id, Team)], [rule_a, rule_b])) assert ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'xxxx', }] } } ) == (ProjectOwnership.Everyone, None) # When fallthrough = False, we don't implicitly assign to Everyone ProjectOwnership.objects.filter( project_id=self.project.id, ).update(fallthrough=False) assert ProjectOwnership.get_owners( self.project.id, { 'sentry.interfaces.Stacktrace': { 'frames': [{ 'filename': 'xxxx', }] } } ) == ([], None)