def get_owners( project: Project, event: Optional["Event"] = None ) -> Iterable[Union["Team", "User"]]: """Given a project and an event, decide which users and teams are the owners.""" if event: owners, _ = ProjectOwnership.get_owners(project.id, event.data) else: owners = ProjectOwnership.Everyone if not owners: outcome = "empty" recipients = set() elif owners == ProjectOwnership.Everyone: outcome = "everyone" recipients = User.objects.filter(id__in=project.member_set.values_list("user", flat=True)) else: outcome = "match" recipients = ActorTuple.resolve_many(owners) metrics.incr( "features.owners.send_to", tags={"organization": project.organization_id, "outcome": outcome}, skip_internal=True, ) return recipients
def get_autoassign_owners(cls, project_id, data, limit=2): """ Get the auto-assign owner for a project if there are any. Returns a tuple of (auto_assignment_enabled, list_of_owners). """ with metrics.timer("projectownership.get_autoassign_owners"): ownership = cls.get_ownership_cached(project_id) if not ownership: return False, [] rules = cls._matching_ownership_rules(ownership, project_id, data) if not rules: return ownership.auto_assignment, [] # We want 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] # Can happen if the ownership rule references a user/team that no longer # is assigned to the project or has been removed from the org. if not actors: return ownership.auto_assignment, [] from sentry.models import ActorTuple return ownership.auto_assignment, ActorTuple.resolve_many(actors)
def get_autoassign_owners(cls, project_id, data, limit=2): """ Get the auto-assign owner for a project if there are any. We combine the schemas from IssueOwners and CodeOwners. Returns a tuple of (auto_assignment_enabled, list_of_owners, assigned_by_codeowners: boolean). """ from sentry.models import ProjectCodeOwners with metrics.timer("projectownership.get_autoassign_owners"): ownership = cls.get_ownership_cached(project_id) codeowners = ProjectCodeOwners.get_codeowners_cached(project_id) assigned_by_codeowners = False if not (ownership or codeowners): return False, [], assigned_by_codeowners if not ownership: ownership = cls(project_id=project_id) ownership_rules = cls._matching_ownership_rules( ownership, project_id, data) codeowners_rules = (cls._matching_ownership_rules( codeowners, project_id, data) if codeowners else []) if not (codeowners_rules or ownership_rules): return ownership.auto_assignment, [], assigned_by_codeowners ownership_actors = cls._find_actors(project_id, ownership_rules, limit) codeowners_actors = cls._find_actors(project_id, codeowners_rules, limit) # Can happen if the ownership rule references a user/team that no longer # is assigned to the project or has been removed from the org. if not (ownership_actors or codeowners_actors): return ownership.auto_assignment, [], assigned_by_codeowners # Ownership rules take precedence over codeowner rules. actors = [*ownership_actors, *codeowners_actors][:limit] # Only the first item in the list is used for assignment, the rest are just used to suggest suspect owners. # So if ownership_actors is empty, it will be assigned by codeowners_actors if len(ownership_actors) == 0: assigned_by_codeowners = True from sentry.models import ActorTuple return ( ownership.auto_assignment, ActorTuple.resolve_many(actors), assigned_by_codeowners, )
def get_owners(project: Project, event: Event | None = None) -> Sequence[Team | User]: """ Given a project and an event, decide which users and teams are the owners. If when checking owners, there is a rule match we only notify the last owner (would-be auto-assignee) unless the organization passes the feature-flag """ if event: owners, _ = ProjectOwnership.get_owners(project.id, event.data) else: owners = ProjectOwnership.Everyone if not owners: outcome = "empty" recipients = list() elif owners == ProjectOwnership.Everyone: outcome = "everyone" recipients = User.objects.filter( id__in=project.member_set.values_list("user", flat=True)) else: outcome = "match" recipients = ActorTuple.resolve_many(owners) # Used to suppress extra notifications to all matched owners, only notify the would-be auto-assignee if not features.has("organizations:notification-all-recipients", project.organization): recipients = recipients[-1:] metrics.incr( "features.owners.send_to", tags={ "organization": project.organization_id, "outcome": outcome }, skip_internal=True, ) return recipients
def get(self, request: Request, project, event_id) -> Response: """ Retrieve suggested owners information for an event `````````````````````````````````````````````````` :pparam string project_slug: the slug of the project the event belongs to. :pparam string event_id: the id of the event. :auth: required """ event = eventstore.get_event_by_id(project.id, event_id) if event is None: return Response({"detail": "Event not found"}, status=404) owners, rules = ProjectOwnership.get_owners(project.id, event.data) # For sake of the API, we don't differentiate between # the implicit "everyone" and no owners if owners == ProjectOwnership.Everyone: owners = [] serialized_owners = serialize(ActorTuple.resolve_many(owners), request.user, ActorSerializer()) # Make sure the serialized owners are in the correct order ordered_owners = [] owner_by_id = {(o["id"], o["type"]): o for o in serialized_owners} for o in owners: key = (str(o.id), "team" if o.type == Team else "user") if owner_by_id.get(key): ordered_owners.append(owner_by_id[key]) return Response({ "owners": ordered_owners, # TODO(mattrobenolt): We need to change the API here to return # all rules, just keeping this way currently for API compat "rule": rules[0].matcher if rules else None, "rules": rules or [], })
def extract_user_ids_from_mentions(organization_id, mentions): """ Extracts user ids from a set of mentions. Mentions should be a list of `ActorTuple` instances. Returns a dictionary with 'users' and 'team_users' keys. 'users' is the user ids for all explicitly mentioned users, and 'team_users' is all user ids from explicitly mentioned teams, excluding any already mentioned users. """ actors = ActorTuple.resolve_many(mentions) actor_mentions = separate_resolved_actors(actors) mentioned_team_users = list( User.objects.get_from_teams( organization_id, actor_mentions["teams"]).exclude( id__in={u.id for u in actor_mentions["users"]}).values_list( "id", flat=True)) return { "users": {user.id for user in actor_mentions["users"]}, "team_users": set(mentioned_team_users), }