예제 #1
0
 def test_get_owners_no_record(self):
     assert ProjectOwnership.get_owners(self.project.id,
                                        {}) == (ProjectOwnership.Everyone,
                                                None)
     assert ProjectOwnership.get_owners(self.project.id,
                                        {}) == (ProjectOwnership.Everyone,
                                                None)
예제 #2
0
    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)
예제 #3
0
파일: event_owners.py 프로젝트: zvrr/sentry
    def get(self, request, project, event_id):
        """
        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)

        # populate event data
        Event.objects.bind_nodes([event], "data")

        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 = []

        return Response({
            "owners":
            serialize(Actor.resolve_many(owners), request.user,
                      ActorSerializer()),
            # 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 [],
        })
예제 #4
0
    def test_get_owners_when_codeowners_exists_and_no_issueowners(self):
        # This case will never exist bc we create a ProjectOwnership record if none exists when creating a ProjectCodeOwner record.
        # We have this testcase for potential corrupt data.
        self.team = self.create_team(organization=self.organization,
                                     slug="tiger-team",
                                     members=[self.user])
        self.code_mapping = self.create_code_mapping(project=self.project)

        rule_a = Rule(Matcher("path", "*.js"), [Owner("team", self.team.slug)])

        self.create_codeowners(
            self.project,
            self.code_mapping,
            raw="*.js @tiger-team",
            schema=dump_schema([rule_a]),
        )
        self.assert_ownership_equals(
            ProjectOwnership.get_owners(
                self.project.id,
                {"stacktrace": {
                    "frames": [{
                        "filename": "src/foo.js"
                    }]
                }}),
            (
                [ActorTuple(self.team.id, Team)],
                [rule_a],
            ),
        )
예제 #5
0
    def test_get_owners_when_codeowners_and_issueowners_exists(self):
        self.team = self.create_team(
            organization=self.organization, slug="tiger-team", members=[self.user]
        )
        self.team2 = self.create_team(
            organization=self.organization, slug="dolphin-team", members=[self.user]
        )
        self.project = self.create_project(
            organization=self.organization, teams=[self.team, self.team2]
        )
        self.code_mapping = self.create_code_mapping(project=self.project)

        rule_a = Rule(Matcher("path", "*.py"), [Owner("team", self.team.slug)])
        rule_b = Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)])
        rule_c = Rule(Matcher("path", "*.py"), [Owner("team", self.team2.slug)])

        ProjectOwnership.objects.create(
            project_id=self.project.id, schema=dump_schema([rule_a, rule_b]), fallthrough=True
        )

        self.create_codeowners(
            self.project, self.code_mapping, raw="*.py @tiger-team", schema=dump_schema([rule_c])
        )

        self.assert_ownership_equals(
            ProjectOwnership.get_owners(
                self.project.id, {"stacktrace": {"frames": [{"filename": "api/foo.py"}]}}
            ),
            (
                [ActorTuple(self.team.id, Team), ActorTuple(self.team2.id, Team)],
                [rule_a, rule_c],
            ),
        )
예제 #6
0
    def get(self, request, project, event_id):
        """
        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
        """
        try:
            event = Event.objects.get(
                id=event_id,
                project_id=project.id,
            )
        except Event.DoesNotExist:
            return Response({'detail': 'Event not found'}, status=404)

        # populate event data
        Event.objects.bind_nodes([event], 'data')

        owners = 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 = []

        return Response(
            serialize(
                Actor.resolve_many(owners),
                request.user,
                ActorSerializer(),
            ))
예제 #7
0
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
예제 #8
0
    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)
예제 #9
0
def get_send_to_owners(
        event: Any, project: Project) -> Mapping[ExternalProviders, Set[User]]:
    owners, _ = ProjectOwnership.get_owners(project.id, event.data)
    if owners == ProjectOwnership.Everyone:
        metrics.incr(
            "features.owners.send_to",
            tags={
                "organization": project.organization_id,
                "outcome": "everyone"
            },
            skip_internal=True,
        )
        return get_send_to_all_in_project(project)

    if not owners:
        metrics.incr(
            "features.owners.send_to",
            tags={
                "organization": project.organization_id,
                "outcome": "empty"
            },
            skip_internal=True,
        )
        return {}

    metrics.incr(
        "features.owners.send_to",
        tags={
            "organization": project.organization_id,
            "outcome": "match"
        },
        skip_internal=True,
    )
    user_ids_to_resolve = set()
    team_ids_to_resolve = set()
    for owner in owners:
        if owner.type == User:
            user_ids_to_resolve.add(owner.id)
        else:
            team_ids_to_resolve.add(owner.id)

    all_possible_users = set()

    if user_ids_to_resolve:
        all_possible_users |= set(
            User.objects.filter(id__in=user_ids_to_resolve))

    # Get all users in teams.
    if team_ids_to_resolve:
        all_possible_users |= get_users_for_teams_to_resolve(
            team_ids_to_resolve)

    mapping: Mapping[
        ExternalProviders,
        Set[User]] = NotificationSetting.objects.filter_to_subscribed_users(
            project, all_possible_users)
    return mapping
예제 #10
0
파일: adapter.py 프로젝트: simPod/sentry
    def get_send_to_owners(self, event, project):
        owners, _ = ProjectOwnership.get_owners(project.id, event.data)
        if owners == ProjectOwnership.Everyone:
            metrics.incr(
                "features.owners.send_to",
                tags={
                    "organization": project.organization_id,
                    "outcome": "everyone"
                },
                skip_internal=True,
            )
            return self.get_send_to_all_in_project(project)

        if not owners:
            metrics.incr(
                "features.owners.send_to",
                tags={
                    "organization": project.organization_id,
                    "outcome": "empty"
                },
                skip_internal=True,
            )
            return {}

        metrics.incr(
            "features.owners.send_to",
            tags={
                "organization": project.organization_id,
                "outcome": "match"
            },
            skip_internal=True,
        )
        all_possible_user_ids = set()
        teams_to_resolve = set()
        for owner in owners:
            if owner.type == User:
                all_possible_user_ids.add(owner.id)
            else:
                teams_to_resolve.add(owner.id)

        # get all users in teams
        if teams_to_resolve:
            all_possible_user_ids |= self.get_user_ids_for_teams_to_resolve(
                teams_to_resolve)
        users = User.objects.filter(id__in=all_possible_user_ids)
        owners_by_provider = NotificationSetting.objects.filter_to_subscribed_users(
            project, users)
        return {
            provider: {user.id
                       for user in users}
            for provider, users in owners_by_provider.items()
        }
예제 #11
0
    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.team.id, Team), Actor(self.user.id, User)], [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
        owner = ProjectOwnership.objects.get(project_id=self.project.id)
        owner.fallthrough = False
        owner.save()

        assert ProjectOwnership.get_owners(
            self.project.id, {"stacktrace": {"frames": [{"filename": "xxxx"}]}}
        ) == ([], None)

        self.assert_ownership_equals(
            ProjectOwnership.get_owners(
                self.project.id, {"stacktrace": {"frames": [{"filename": "src/foo.py"}]}}
            ),
            ([Actor(self.team.id, Team), Actor(self.user.id, User)], [rule_a, rule_b]),
        )
예제 #12
0
    def get_send_to_owners(self, event, project):
        owners, _ = ProjectOwnership.get_owners(project.id, event.data)
        if owners == ProjectOwnership.Everyone:
            metrics.incr(
                "features.owners.send_to",
                tags={
                    "organization": project.organization_id,
                    "outcome": "everyone"
                },
                skip_internal=True,
            )
            return self.get_send_to_all_in_project(project)

        if not owners:
            metrics.incr(
                "features.owners.send_to",
                tags={
                    "organization": project.organization_id,
                    "outcome": "empty"
                },
                skip_internal=True,
            )
            return {}

        metrics.incr(
            "features.owners.send_to",
            tags={
                "organization": project.organization_id,
                "outcome": "match"
            },
            skip_internal=True,
        )
        all_possible_user_ids = set()
        teams_to_resolve = set()
        for owner in owners:
            if owner.type == User:
                all_possible_user_ids.add(owner.id)
            else:
                teams_to_resolve.add(owner.id)

        # get all users in teams
        if teams_to_resolve:
            all_possible_user_ids |= self.get_user_ids_for_teams_to_resolve(
                teams_to_resolve)

        output = defaultdict(set)
        disabled_users = self.disabled_users_from_project(project)

        for provider in EXTERNAL_PROVIDERS.keys():
            output[provider] = all_possible_user_ids - disabled_users[provider]
        return output
예제 #13
0
파일: adapter.py 프로젝트: liang0/sentry-1
    def get_send_to_owners(self, event, project):
        owners, _ = ProjectOwnership.get_owners(project.id, event.data)
        if owners != ProjectOwnership.Everyone:
            if not owners:
                metrics.incr(
                    "features.owners.send_to",
                    tags={
                        "organization": project.organization_id,
                        "outcome": "empty"
                    },
                    skip_internal=True,
                )
                return set()

            metrics.incr(
                "features.owners.send_to",
                tags={
                    "organization": project.organization_id,
                    "outcome": "match"
                },
                skip_internal=True,
            )
            send_to = set()
            teams_to_resolve = set()
            for owner in owners:
                if owner.type == User:
                    send_to.add(owner.id)
                else:
                    teams_to_resolve.add(owner.id)

            # get all users in teams
            if teams_to_resolve:
                send_to |= set(
                    User.objects.filter(
                        is_active=True,
                        sentry_orgmember_set__organizationmemberteam__team__id__in
                        =teams_to_resolve,
                    ).values_list("id", flat=True))

            return send_to - self.disabled_users_from_project(project)
        else:
            metrics.incr(
                "features.owners.send_to",
                tags={
                    "organization": project.organization_id,
                    "outcome": "everyone"
                },
                skip_internal=True,
            )
            return self.get_send_to_all_in_project(project)
예제 #14
0
 def test_abs_path_when_filename_present(self):
     frame = {
         "filename": "computer.cpp",
         "abs_path": "C:\\My\\Path\\computer.cpp",
     }
     rule = Rule(Matcher("path", "*My\\Path*"),
                 [Owner("team", self.team.slug)])
     ProjectOwnership.objects.create(project_id=self.project.id,
                                     schema=dump_schema([rule]),
                                     fallthrough=True)
     assert ProjectOwnership.get_owners(
         self.project.id, {"stacktrace": {
             "frames": [frame]
         }}) == ([ActorTuple(self.team.id, Team)], [rule])
예제 #15
0
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
예제 #16
0
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 inefficent.
        # 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 efficency 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
예제 #17
0
def build_events_by_actor(
        project_id: int, events: Iterable[Event],
        user_ids: Iterable[int]) -> Mapping[ActorTuple, Iterable[Event]]:
    """
    TODO(mgaeta): 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.
    """
    events_by_actor: MutableMapping[ActorTuple, Set[Event]] = defaultdict(set)
    for event in events:
        actors, __ = ProjectOwnership.get_owners(project_id, event.data)
        if actors == ProjectOwnership.Everyone:
            actors = [ActorTuple(user_id, User) for user_id in user_ids]
        for actor in actors:
            events_by_actor[actor].add(event)
    return events_by_actor
예제 #18
0
    def get(self, request, project, event_id):
        """
        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)

        # populate event data
        if not options.get("eventstore.use-nodestore"):
            event.bind_node_data()

        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(Actor.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 = (six.text_type(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 [],
        })
예제 #19
0
    def get(self, request, project, event_id):
        """
        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
        """

        use_snuba = options.get('snuba.events-queries.enabled')

        event_cls = SnubaEvent if use_snuba else Event

        event = event_cls.objects.from_event_id(event_id, project.id)
        if event is None:
            return Response({'detail': 'Event not found'}, status=404)

        # populate event data
        Event.objects.bind_nodes([event], 'data')

        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 = []

        return Response({
            'owners':
            serialize(
                Actor.resolve_many(owners),
                request.user,
                ActorSerializer(),
            ),
            # 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 [],
        })
예제 #20
0
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
예제 #21
0
    def get(self, request, project, event_id):
        """
        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
        """

        use_snuba = options.get('snuba.events-queries.enabled')

        event_cls = SnubaEvent if use_snuba else Event

        event = event_cls.objects.from_event_id(event_id, project.id)
        if event is None:
            return Response({'detail': 'Event not found'}, status=404)

        # populate event data
        Event.objects.bind_nodes([event], 'data')

        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 = []

        return Response({
            'owners': serialize(
                Actor.resolve_many(owners),
                request.user,
                ActorSerializer(),
            ),
            # 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 [],
        })
예제 #22
0
    def get(self, request, project, event_id):
        """
        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
        """
        try:
            event = Event.objects.get(
                id=event_id,
                project_id=project.id,
            )
        except Event.DoesNotExist:
            return Response({'detail': 'Event not found'}, status=404)

        # populate event data
        Event.objects.bind_nodes([event], 'data')

        owners, matcher = 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 = []

        return Response({
            'owners': serialize(
                Actor.resolve_many(owners),
                request.user,
                ActorSerializer(),
            ),
            'rule': matcher,
        })
예제 #23
0
파일: models.py 프로젝트: alexandrul/sentry
    def get_send_to(self, project, event=None):
        """
        Returns a list of user IDs for the users that should receive
        notifications for the provided project.

        This result may come from cached data.
        """
        if not (project and project.teams.exists()):
            logger.debug('Tried to send notification to invalid project: %r', project)
            return []

        if event:
            owners, _ = ProjectOwnership.get_owners(project.id, event.data)
            if owners != ProjectOwnership.Everyone:
                if not owners:
                    metrics.incr(
                        'features.owners.send_to',
                        tags={
                            'organization': project.organization_id,
                            'outcome': 'empty',
                        },
                        skip_internal=True,
                    )
                    return []

                metrics.incr(
                    'features.owners.send_to',
                    tags={
                        'organization': project.organization_id,
                        'outcome': 'match',
                    },
                    skip_internal=True,
                )
                send_to_list = []
                teams_to_resolve = []
                for owner in owners:
                    if owner.type == User:
                        send_to_list.append(owner.id)
                    else:
                        teams_to_resolve.append(owner.id)

                # get all users in teams
                if teams_to_resolve:
                    send_to_list += User.objects.filter(
                        is_active=True,
                        sentry_orgmember_set__organizationmemberteam__team__id__in=teams_to_resolve,
                    ).values_list('id', flat=True)
                return send_to_list
            else:
                metrics.incr(
                    'features.owners.send_to',
                    tags={
                        'organization': project.organization_id,
                        'outcome': 'everyone',
                    },
                    skip_internal=True,
                )

        cache_key = '%s:send_to:%s' % (self.get_conf_key(), project.pk)
        send_to_list = cache.get(cache_key)
        if send_to_list is None:
            send_to_list = [s for s in self.get_sendable_users(project) if s]
            cache.set(cache_key, send_to_list, 60)  # 1 minute cache

        return send_to_list
예제 #24
0
    def get_send_to(self, project, event=None):
        """
        Returns a list of user IDs for the users that should receive
        notifications for the provided project.

        This result may come from cached data.
        """
        if not (project and project.teams.exists()):
            logger.debug('Tried to send notification to invalid project: %r',
                         project)
            return []

        if event:
            owners, _ = ProjectOwnership.get_owners(project.id, event.data)
            if owners != ProjectOwnership.Everyone:
                if not owners:
                    metrics.incr(
                        'features.owners.send_to',
                        tags={
                            'organization': project.organization_id,
                            'outcome': 'empty',
                        },
                        skip_internal=True,
                    )
                    return []

                metrics.incr(
                    'features.owners.send_to',
                    tags={
                        'organization': project.organization_id,
                        'outcome': 'match',
                    },
                    skip_internal=True,
                )
                send_to_list = []
                teams_to_resolve = []
                for owner in owners:
                    if owner.type == User:
                        send_to_list.append(owner.id)
                    else:
                        teams_to_resolve.append(owner.id)

                # get all users in teams
                if teams_to_resolve:
                    send_to_list += User.objects.filter(
                        is_active=True,
                        sentry_orgmember_set__organizationmemberteam__team__id__in
                        =teams_to_resolve,
                    ).values_list('id', flat=True)
                return send_to_list
            else:
                metrics.incr(
                    'features.owners.send_to',
                    tags={
                        'organization': project.organization_id,
                        'outcome': 'everyone',
                    },
                    skip_internal=True,
                )

        cache_key = '%s:send_to:%s' % (self.get_conf_key(), project.pk)
        send_to_list = cache.get(cache_key)
        if send_to_list is None:
            send_to_list = [s for s in self.get_sendable_users(project) if s]
            cache.set(cache_key, send_to_list, 60)  # 1 minute cache

        return send_to_list
예제 #25
0
def get_send_to_owners(
    event: "Event", project: Project
) -> Mapping[ExternalProviders, Union[Set[User], Set[Team]]]:
    owners, _ = ProjectOwnership.get_owners(project.id, event.data)
    if owners == ProjectOwnership.Everyone:
        metrics.incr(
            "features.owners.send_to",
            tags={
                "organization": project.organization_id,
                "outcome": "everyone"
            },
            skip_internal=True,
        )
        return get_send_to_all_in_project(project)

    if not owners:
        metrics.incr(
            "features.owners.send_to",
            tags={
                "organization": project.organization_id,
                "outcome": "empty"
            },
            skip_internal=True,
        )
        return {}

    metrics.incr(
        "features.owners.send_to",
        tags={
            "organization": project.organization_id,
            "outcome": "match"
        },
        skip_internal=True,
    )
    user_ids_to_resolve = set()
    team_ids_to_resolve = set()
    for owner in owners:
        if owner.type == User:
            user_ids_to_resolve.add(owner.id)
        else:
            team_ids_to_resolve.add(owner.id)

    all_possible_users = set()

    if user_ids_to_resolve:
        all_possible_users |= set(
            User.objects.filter(id__in=user_ids_to_resolve))

    team_mapping: Dict[ExternalProviders, Set[Team]] = {
        ExternalProviders.SLACK: set()
    }
    team_ids_to_remove = set()
    if team_ids_to_resolve:
        # check for team Slack settings. if present, notify there instead
        for team_id in team_ids_to_resolve:
            team = Team.objects.get(id=team_id)
            team_slack_settings = NotificationSetting.objects.get_settings(
                provider=ExternalProviders.SLACK,
                type=NotificationSettingTypes.ISSUE_ALERTS,
                team=team,
            )
            if team_slack_settings == NotificationSettingOptionValues.ALWAYS:
                team_mapping[ExternalProviders.SLACK].add(team)
                team_ids_to_remove.add(team_id)
        # Get all users in teams that don't have Slack settings.
        team_ids_to_resolve -= team_ids_to_remove
        all_possible_users |= get_users_for_teams_to_resolve(
            team_ids_to_resolve)
    mapping: MutableMapping[ExternalProviders, Union[
        Set[User],
        Set[Team]]] = NotificationSetting.objects.filter_to_subscribed_users(
            project, all_possible_users)

    if not mapping:
        return team_mapping

    # combine the user and team mappings
    if team_mapping:
        for provider in set.union(set(team_mapping.keys()),
                                  set(mapping.keys())):
            if mapping.get(provider) and team_mapping.get(provider):
                mapping[provider].update(list(team_mapping[provider]))
            else:
                if not mapping.get(provider) and team_mapping.get(provider):
                    mapping[provider] = team_mapping[provider]
    return mapping
예제 #26
0
 def test_get_owners_default(self):
     assert ProjectOwnership.get_owners(self.project.id,
                                        {}) == (ProjectOwnership.Everyone,
                                                None)
예제 #27
0
파일: models.py 프로젝트: yxlbeyond/sentry
    def get_send_to(self, project, event=None):
        """
        Returns a list of user IDs for the users that should receive
        notifications for the provided project.

        This result may come from cached data.
        """
        if not (project and project.teams.exists()):
            logger.debug("Tried to send notification to invalid project: %r",
                         project)
            return []

        if event:
            owners, _ = ProjectOwnership.get_owners(project.id, event.data)
            if owners != ProjectOwnership.Everyone:
                if not owners:
                    metrics.incr(
                        "features.owners.send_to",
                        tags={
                            "organization": project.organization_id,
                            "outcome": "empty"
                        },
                        skip_internal=True,
                    )
                    return []

                metrics.incr(
                    "features.owners.send_to",
                    tags={
                        "organization": project.organization_id,
                        "outcome": "match"
                    },
                    skip_internal=True,
                )
                send_to_list = set()
                teams_to_resolve = set()
                for owner in owners:
                    if owner.type == User:
                        send_to_list.add(owner.id)
                    else:
                        teams_to_resolve.add(owner.id)

                # get all users in teams
                if teams_to_resolve:
                    send_to_list |= set(
                        User.objects.filter(
                            is_active=True,
                            sentry_orgmember_set__organizationmemberteam__team__id__in
                            =teams_to_resolve,
                        ).values_list("id", flat=True))

                alert_settings = project.get_member_alert_settings(
                    self.alert_option_key)
                disabled_users = set(
                    user for user, setting in alert_settings.items()
                    if setting == 0)
                return send_to_list - disabled_users
            else:
                metrics.incr(
                    "features.owners.send_to",
                    tags={
                        "organization": project.organization_id,
                        "outcome": "everyone"
                    },
                    skip_internal=True,
                )

        cache_key = "%s:send_to:%s" % (self.get_conf_key(), project.pk)
        send_to_list = cache.get(cache_key)
        if send_to_list is None:
            send_to_list = [s for s in self.get_sendable_users(project) if s]
            cache.set(cache_key, send_to_list, 60)  # 1 minute cache

        return send_to_list
예제 #28
0
    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)
예제 #29
0
    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)
예제 #30
0
 def test_get_owners_default(self):
     assert ProjectOwnership.get_owners(self.project.id, {}) == (ProjectOwnership.Everyone, None)