def get_owners(cls, project_id, data): """ For a given project_id, and event data blob. We combine the schemas from IssueOwners and CodeOwners. If Everyone is returned, this means we implicitly are falling through our rules and everyone is responsible. If an empty list is returned, this means there are explicitly no owners. """ from sentry.models import ProjectCodeOwners ownership = cls.get_ownership_cached(project_id) if not ownership: ownership = cls(project_id=project_id) codeowners = ProjectCodeOwners.get_codeowners_cached(project_id) ownership.schema = cls.get_combined_schema(ownership, codeowners) rules = cls._matching_ownership_rules(ownership, project_id, data) if not rules: return cls.Everyone if ownership.fallthrough else [], None owners = {o for rule in rules for o in rule.owners} owners_to_actors = resolve_actors(owners, project_id) ordered_actors = [] for rule in rules: for o in rule.owners: if o in owners and owners_to_actors.get(o) is not None: ordered_actors.append(owners_to_actors[o]) owners.remove(o) return ordered_actors, rules
def test_basic(self): owners = [ Owner("user", self.user.email), Owner("team", self.team.slug) ] assert resolve_actors(owners, self.project.id) == { owners[0]: ActorTuple(self.user.id, User), owners[1]: ActorTuple(self.team.id, Team), }
def _find_actors(cls, project_id, rules, limit): """ Get the last matching rule to take the most precedence. """ owners = [owner for rule in rules for owner in rule.owners] owners.reverse() actors = { key: val for key, val in resolve_actors({owner for owner in owners}, project_id).items() if val } actors = [actors[owner] for owner in owners if owner in actors][:limit] return actors
def get_owners( cls, project_id: int, data: Mapping[str, Any] ) -> Tuple[Union["Everyone", Sequence["ActorTuple"]], Optional[Sequence[Rule]]]: """ For a given project_id, and event data blob. We combine the schemas from IssueOwners and CodeOwners. If there are no matching rules, check ProjectOwnership.fallthrough: If ProjectOwnership.fallthrough is enabled, return Everyone (all project members) - we implicitly are falling through our rules and everyone is responsible. If ProjectOwnership.fallthrough is disabled, return an empty list - there are explicitly no owners If there are matching rules, return the ordered actors. The order is determined by iterating through rules sequentially, evaluating CODEOWNERS (if present), followed by Ownership Rules """ from sentry.models import ProjectCodeOwners ownership = cls.get_ownership_cached(project_id) if not ownership: ownership = cls(project_id=project_id) codeowners = ProjectCodeOwners.get_codeowners_cached(project_id) ownership.schema = cls.get_combined_schema(ownership, codeowners) rules = cls._matching_ownership_rules(ownership, project_id, data) if not rules: return cls.Everyone if ownership.fallthrough else [], None owners = {o for rule in rules for o in rule.owners} owners_to_actors = resolve_actors(owners, project_id) ordered_actors = [] for rule in rules: for o in rule.owners: if o in owners and owners_to_actors.get(o) is not None: ordered_actors.append(owners_to_actors[o]) owners.remove(o) return ordered_actors, rules
def test_teams(self): # Normal team owner1 = Owner("team", self.team.slug) actor1 = ActorTuple(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 = ActorTuple(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_no_actors(self): assert resolve_actors([], self.project.id) == {}