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],
            ),
        )
    def test_get_autoassign_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]))

        # No autoassignment on match
        assert ProjectOwnership.get_autoassign_owners(
            self.project.id,
            {"stacktrace": {
                "frames": [{
                    "filename": "api/foo.py"
                }]
            }}) == (False, [self.team, self.team2], False)
        # autoassignment is True
        owner = ProjectOwnership.objects.get(project_id=self.project.id)
        owner.auto_assignment = True
        owner.save()

        assert ProjectOwnership.get_autoassign_owners(
            self.project.id,
            {"stacktrace": {
                "frames": [{
                    "filename": "api/foo.py"
                }]
            }}) == (True, [self.team, self.team2], False)

        # # more than 2 matches
        assert ProjectOwnership.get_autoassign_owners(
            self.project.id,
            {"stacktrace": {
                "frames": [{
                    "filename": "src/foo.py"
                }]
            }}) == (True, [self.user, self.team], False)
    def test_get_autoassign_owners_only_issueowners_exists(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]),
        )

        # No data matches
        assert ProjectOwnership.get_autoassign_owners(self.project.id,
                                                      {}) == (False, [], False)

        # No autoassignment on match
        assert ProjectOwnership.get_autoassign_owners(
            self.project.id,
            {"stacktrace": {
                "frames": [{
                    "filename": "foo.py"
                }]
            }}) == (False, [self.team], False)

        # autoassignment is True
        owner = ProjectOwnership.objects.get(project_id=self.project.id)
        owner.auto_assignment = True
        owner.save()

        assert ProjectOwnership.get_autoassign_owners(
            self.project.id,
            {"stacktrace": {
                "frames": [{
                    "filename": "foo.py"
                }]
            }}) == (True, [self.team], False)
    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],
            ),
        )
Exemple #5
0
    def validate_raw(self, attrs, source):
        if not attrs[source].strip():
            return attrs
        try:
            rules = parse_rules(attrs[source])
        except ParseError as e:
            raise serializers.ValidationError(
                u'Parse error: %r (line %d, column %d)' %
                (e.expr.name, e.line(), e.column()))

        schema = dump_schema(rules)

        bad_actors = []
        for rule in rules:
            for owner in rule.owners:
                try:
                    resolve_actor(owner, self.context['ownership'].project_id)
                except UnknownActor:
                    if owner.type == 'user':
                        bad_actors.append(owner.identifier)

                    if owner.type == 'team':
                        bad_actors.append(u'#{}'.format(owner.identifier))

        if bad_actors:
            raise serializers.ValidationError(
                u'Invalid rule owners: {}'.format(", ".join(bad_actors)))

        attrs['schema'] = schema
        return attrs
    def test_multiple_owners_order_matters(self):
        users = [self.user, self.user2, self.user3]
        rules = [
            Rule(Matcher("path", "*.py"), [Owner("user", users[0].email)]),
            Rule(Matcher("path", "*foo*"), [Owner("user", users[1].email)]),
            Rule(Matcher("path", "*"), [Owner("user", users[2].email)]),
        ]
        rules.reverse()

        ProjectOwnership.objects.create(
            project_id=self.project.id, schema=dump_schema(rules), fallthrough=True
        )

        event1 = self.store_event(
            data={"stacktrace": {"frames": [{"filename": "foo.py"}]}}, project_id=self.project.id
        )

        self.path = reverse(
            "sentry-api-0-event-owners",
            kwargs={
                "organization_slug": self.organization.slug,
                "project_slug": self.project.slug,
                "event_id": event1.event_id,
            },
        )

        resp = self.client.get(self.path)
        assert resp.status_code == 200
        assert len(resp.data["owners"]) == 3
        assert [o["id"] for o in resp.data["owners"]] == [str(u.id) for u in reversed(users)]
        assert resp.data["rule"] == Matcher("path", "*")
        assert len(resp.data["rules"]) == 3
Exemple #7
0
    def validate_raw(self, attrs, source):
        if not attrs[source].strip():
            return attrs
        try:
            rules = parse_rules(attrs[source])
        except ParseError as e:
            raise serializers.ValidationError(
                u'Parse error: %r (line %d, column %d)' % (
                    e.expr.name, e.line(), e.column()
                ))

        schema = dump_schema(rules)

        owners = {o for rule in rules for o in rule.owners}
        actors = resolve_actors(owners, self.context['ownership'].project_id)

        bad_actors = []
        for owner, actor in six.iteritems(actors):
            if actor is None:
                if owner.type == 'user':
                    bad_actors.append(owner.identifier)
                elif owner.type == 'team':
                    bad_actors.append(u'#{}'.format(owner.identifier))

        if bad_actors:
            raise serializers.ValidationError(
                u'Invalid rule owners: {}'.format(", ".join(bad_actors))
            )

        attrs['schema'] = schema
        return attrs
Exemple #8
0
    def test_one_owner(self):
        rule_a = Rule(Matcher("path", "*.py"),
                      [Owner("user", self.user.email)])

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

        event1 = self.store_event(
            data={"stacktrace": {
                "frames": [{
                    "filename": "foo.py"
                }]
            }},
            project_id=self.project.id)

        self.path = reverse(
            "sentry-api-0-event-owners",
            kwargs={
                "organization_slug": self.organization.slug,
                "project_slug": self.project.slug,
                "event_id": event1.event_id,
            },
        )

        resp = self.client.get(self.path)
        assert resp.status_code == 200
        assert len(resp.data["owners"]) == 1
        assert resp.data["owners"][0]["id"] == six.text_type(self.user.id)
        assert resp.data["rule"] == Matcher("path", "*.py")
        assert len(resp.data["rules"]) == 1
Exemple #9
0
    def test_matching_non_existing_owner(self):
        rule_a = Rule(Matcher("path", "*"),
                      [Owner("user", "*****@*****.**")])

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

        event1 = self.store_event(
            data={"stacktrace": {
                "frames": [{
                    "filename": "foo.py"
                }]
            }},
            project_id=self.project.id)

        self.path = reverse(
            "sentry-api-0-event-owners",
            kwargs={
                "organization_slug": self.organization.slug,
                "project_slug": self.project.slug,
                "event_id": event1.event_id,
            },
        )

        resp = self.client.get(self.path)
        assert resp.status_code == 200
        assert len(resp.data["owners"]) == 0
        assert resp.data["rule"] == Matcher(type="path", pattern="*")
        assert len(resp.data["rules"]) == 1
Exemple #10
0
    def validate(self, attrs):
        if not attrs.get("raw", "").strip():
            return attrs
        try:
            rules = parse_rules(attrs["raw"])
        except ParseError as e:
            raise serializers.ValidationError({
                "raw":
                "Parse error: %r (line %d, column %d)" %
                (e.expr.name, e.line(), e.column())
            })

        schema = dump_schema(rules)

        owners = {o for rule in rules for o in rule.owners}
        actors = resolve_actors(owners, self.context["ownership"].project_id)

        bad_actors = []
        for owner, actor in actors.items():
            if actor is None:
                if owner.type == "user":
                    bad_actors.append(owner.identifier)
                elif owner.type == "team":
                    bad_actors.append(f"#{owner.identifier}")

        if bad_actors:
            bad_actors.sort()
            raise serializers.ValidationError({
                "raw":
                "Invalid rule owners: {}".format(", ".join(bad_actors))
            })

        attrs["schema"] = schema
        return attrs
Exemple #11
0
    def setUp(self):
        self.user2 = self.create_user(email="*****@*****.**", is_active=True)
        self.user3 = self.create_user(email="*****@*****.**", is_active=True)

        self.team2 = self.create_team(organization=self.organization,
                                      members=[self.user, self.user2])
        self.project.add_team(self.team2)

        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema([
                grammar.Rule(Matcher("path", "*.py"),
                             [Owner("team", self.team2.slug)]),
                grammar.Rule(Matcher("path", "*.jsx"),
                             [Owner("user", self.user.email)]),
                grammar.Rule(Matcher("path", "*.jx"),
                             [Owner("user", self.user3.email)]),
                grammar.Rule(
                    Matcher("path", "*.cbl"),
                    [
                        Owner("user", user.email)
                        for user in User.objects.filter(
                            id__in=self.project.member_set.values_list(
                                "user", flat=True))
                    ],
                ),
                grammar.Rule(Matcher("path", "*.lol"), []),
            ]),
            fallthrough=True,
        )
    def test_owners_of_different_types_ordered_correctly(self):
        owners = [self.user, self.team3, self.user2, self.team2, self.user3, self.team]
        rules = [
            Rule(Matcher("path", "*.py"), [Owner("user", owners[0].email)]),
            Rule(Matcher("path", "*py"), [Owner("team", owners[1].slug)]),
            Rule(Matcher("path", "*foo*"), [Owner("user", owners[2].email)]),
            Rule(Matcher("path", "*y"), [Owner("team", owners[3].slug)]),
            Rule(Matcher("path", "*"), [Owner("user", owners[4].email)]),
            Rule(Matcher("path", "*o.py"), [Owner("team", owners[5].slug)]),
        ]

        ProjectOwnership.objects.create(
            project_id=self.project.id, schema=dump_schema(rules), fallthrough=True
        )

        event1 = self.store_event(
            data={"stacktrace": {"frames": [{"filename": "foo.py"}]}}, project_id=self.project.id
        )

        self.path = reverse(
            "sentry-api-0-event-owners",
            kwargs={
                "organization_slug": self.organization.slug,
                "project_slug": self.project.slug,
                "event_id": event1.event_id,
            },
        )

        resp = self.client.get(self.path)
        assert resp.status_code == 200
        assert len(resp.data["owners"]) == 6
        assert [o["id"] for o in resp.data["owners"]] == [str(o.id) for o in owners]
        assert [o["type"] for o in resp.data["owners"]] == ["user", "team"] * 3
        assert resp.data["rule"] == Matcher("path", "*.py")
        assert len(resp.data["rules"]) == 6
    def test_get_autoassign_owners_only_codeowners_exists(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]),
        )
        # No data matches
        assert ProjectOwnership.get_autoassign_owners(self.project.id,
                                                      {}) == (False, [], False)

        # No autoassignment on match
        assert ProjectOwnership.get_autoassign_owners(
            self.project.id,
            {"stacktrace": {
                "frames": [{
                    "filename": "foo.js"
                }]
            }}) == (False, [self.team], True)
Exemple #14
0
    def validate(self, attrs):
        if not attrs.get('raw', '').strip():
            return attrs
        try:
            rules = parse_rules(attrs['raw'])
        except ParseError as e:
            raise serializers.ValidationError({
                'raw':
                u'Parse error: %r (line %d, column %d)' %
                (e.expr.name, e.line(), e.column())
            })

        schema = dump_schema(rules)

        owners = {o for rule in rules for o in rule.owners}
        actors = resolve_actors(owners, self.context['ownership'].project_id)

        bad_actors = []
        for owner, actor in six.iteritems(actors):
            if actor is None:
                if owner.type == 'user':
                    bad_actors.append(owner.identifier)
                elif owner.type == 'team':
                    bad_actors.append(u'#{}'.format(owner.identifier))

        if bad_actors:
            raise serializers.ValidationError({
                'raw':
                u'Invalid rule owners: {}'.format(", ".join(bad_actors))
            })

        attrs['schema'] = schema
        return attrs
Exemple #15
0
    def setUp(self):
        self.user = self.create_user(email="*****@*****.**", is_active=True)
        self.user2 = self.create_user(email="*****@*****.**", is_active=True)
        self.organization = self.create_organization(owner=self.user)
        self.team = self.create_team(organization=self.organization)

        self.project = self.create_project(name="Test", teams=[self.team])
        OrganizationMemberTeam.objects.create(
            organizationmember=OrganizationMember.objects.get(
                user=self.user, organization=self.organization),
            team=self.team,
        )
        self.create_member(user=self.user2,
                           organization=self.organization,
                           teams=[self.team])
        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema([
                grammar.Rule(Matcher("path", "*.py"),
                             [Owner("team", self.team.slug)]),
                grammar.Rule(Matcher("path", "*.jx"),
                             [Owner("user", self.user2.email)]),
                grammar.Rule(
                    Matcher("path", "*.cbl"),
                    [
                        Owner("user", self.user.email),
                        Owner("user", self.user2.email)
                    ],
                ),
            ]),
            fallthrough=True,
        )
Exemple #16
0
    def test_notify_users_with_owners(self):
        user = self.create_user(email="*****@*****.**", is_active=True)
        user2 = self.create_user(email="*****@*****.**", is_active=True)

        organization = self.create_organization(owner=user)
        team = self.create_team(organization=organization)
        project = self.create_project(name="Test", teams=[team])
        OrganizationMemberTeam.objects.create(
            organizationmember=OrganizationMember.objects.get(user=user, organization=organization),
            team=team,
        )
        self.create_member(user=user2, organization=organization, teams=[team])
        self.group = self.create_group(
            first_seen=timezone.now(),
            last_seen=timezone.now(),
            project=project,
            message="hello  world",
            logger="root",
        )
        ProjectOwnership.objects.create(
            project_id=project.id,
            schema=dump_schema(
                [
                    grammar.Rule(Matcher("path", "*.py"), [Owner("team", team.slug)]),
                    grammar.Rule(Matcher("path", "*.jx"), [Owner("user", user2.email)]),
                    grammar.Rule(
                        Matcher("path", "*.cbl"),
                        [Owner("user", user.email), Owner("user", user2.email)],
                    ),
                ]
            ),
            fallthrough=True,
        )

        event_all_users = self.store_event(
            data=self.make_event_data("foo.cbl"), project_id=project.id
        )
        self.assert_notify(event_all_users, [user.email, user2.email])

        event_team = self.store_event(data=self.make_event_data("foo.py"), project_id=project.id)
        self.assert_notify(event_team, [user.email, user2.email])

        event_single_user = self.store_event(
            data=self.make_event_data("foo.jx"), project_id=project.id
        )
        self.assert_notify(event_single_user, [user2.email])

        # Make sure that disabling mail alerts works as expected
        NotificationSetting.objects.update_settings(
            ExternalProviders.EMAIL,
            NotificationSettingTypes.ISSUE_ALERTS,
            NotificationSettingOptionValues.NEVER,
            user=user2,
            project=project,
        )
        event_all_users = self.store_event(
            data=self.make_event_data("foo.cbl"), project_id=project.id
        )
        self.assert_notify(event_all_users, [user.email])
Exemple #17
0
 def create_ownership(
     self, project: Project, rules: Optional[Sequence[Rule]] = None, fallthrough: bool = False
 ) -> ProjectOwnership:
     return ProjectOwnership.objects.create(
         project_id=project.id,
         schema=dump_schema(rules if rules else []),
         fallthrough=fallthrough,
     )
Exemple #18
0
    def test_owner_assignment_when_owners_have_been_unassigned(self):
        """
        Test that ensures that if certain assignees get unassigned, and project rules are changed
        then the new group assignees should be re-calculated and re-assigned
        """
        # Create rules and check assignees
        self.make_ownership()
        event = self.store_event(
            data={
                "message": "oh no",
                "platform": "python",
                "stacktrace": {
                    "frames": [{
                        "filename": "src/app/example.py"
                    }]
                },
            },
            project_id=self.project.id,
        )
        cache_key = write_event_to_cache(event)
        post_process_group(
            is_new=False,
            is_regression=False,
            is_new_group_environment=False,
            cache_key=cache_key,
            group_id=event.group_id,
        )
        assignee = event.group.assignee_set.first()
        assert assignee.user == self.user

        user_3 = self.create_user()
        self.create_team_membership(self.team, user=user_3)

        # De-assign group assignees
        GroupAssignee.objects.deassign(event.group, self.user)
        assert event.group.assignee_set.first() is None

        # Change ProjectOwnership rules
        rules = [
            Rule(Matcher("path", "src/*"), [Owner("user", user_3.email)]),
        ]
        self.prj_ownership.schema = dump_schema(rules)
        self.prj_ownership.save()

        cache_key = write_event_to_cache(event)
        post_process_group(
            is_new=False,
            is_regression=False,
            is_new_group_environment=False,
            cache_key=cache_key,
            group_id=event.group_id,
        )

        # Group should be re-assigned to the new group owner
        assignee = event.group.assignee_set.first()
        assert assignee.user == user_3
Exemple #19
0
def test_dump_schema():
    assert dump_schema([Rule(Matcher("path", "*.js"), [Owner("team", "frontend")])]) == {
        "$version": 1,
        "rules": [
            {
                "matcher": {"type": "path", "pattern": "*.js"},
                "owners": [{"type": "team", "identifier": "frontend"}],
            }
        ],
    }
Exemple #20
0
    def make_ownership(self):
        rule_a = Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)])
        rule_b = Rule(Matcher("path", "tests/*"), [Owner("team", self.team.name)])
        rule_c = Rule(Matcher("path", "src/app/*"), [Owner("team", self.team.name)])

        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema([rule_a, rule_b, rule_c]),
            fallthrough=True,
            auto_assignment=True,
        )
Exemple #21
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]),
        )
Exemple #22
0
 def validate_raw(self, attrs, source):
     if not attrs[source].strip():
         return attrs
     try:
         rules = parse_rules(attrs[source])
     except ParseError as e:
         raise serializers.ValidationError(
             u'Parse error: %r (line %d, column %d)' % (
                 e.expr.name, e.line(), e.column()
             ))
     attrs['schema'] = dump_schema(rules)
     return attrs
    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_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])
Exemple #25
0
    def make_ownership(self, extra_rules=None):
        rules = [
            Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)]),
            Rule(Matcher("path", "tests/*"), [Owner("team", self.team.name)]),
            Rule(Matcher("path", "src/app/*"), [Owner("team", self.team.name)]),
        ]
        if extra_rules:
            rules.extend(extra_rules)

        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema(rules),
            fallthrough=True,
            auto_assignment=True,
        )
Exemple #26
0
def test_dump_schema():
    assert dump_schema(
        [Rule(Matcher('path', '*.js'), [Owner('team', 'frontend')])]) == {
            '$version':
            1,
            'rules': [{
                'matcher': {
                    'type': 'path',
                    'pattern': '*.js',
                },
                'owners': [{
                    'type': 'team',
                    'identifier': 'frontend',
                }]
            }]
        }
Exemple #27
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)
Exemple #28
0
    def make_ownership(self):
        rule_a = Rule(Matcher('path', 'src/*'), [
            Owner('user', self.user.email),
        ])
        rule_b = Rule(Matcher('path', 'tests/*'), [
            Owner('team', self.team.name),
        ])
        rule_c = Rule(Matcher('path', 'src/app/*'), [
            Owner('team', self.team.name),
        ])

        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema([rule_a, rule_b, rule_c]),
            fallthrough=True,
            auto_assignment=True,
        )
Exemple #29
0
 def setUp(self):
     self.user2 = self.create_user(email="*****@*****.**", is_active=True)
     self.create_member(user=self.user2, organization=self.organization, teams=[self.team])
     ProjectOwnership.objects.create(
         project_id=self.project.id,
         schema=dump_schema(
             [
                 grammar.Rule(Matcher("path", "*.py"), [Owner("team", self.team.slug)]),
                 grammar.Rule(Matcher("path", "*.jx"), [Owner("user", self.user2.email)]),
                 grammar.Rule(
                     Matcher("path", "*.cbl"),
                     [Owner("user", self.user.email), Owner("user", self.user2.email)],
                 ),
             ]
         ),
         fallthrough=True,
     )
Exemple #30
0
def test_dump_schema():
    assert dump_schema([Rule(
        Matcher('path', '*.js'),
        [Owner('team', 'frontend')]
    )]) == {
        '$version': 1,
        'rules': [{
            'matcher': {
                'type': 'path',
                'pattern': '*.js',
            },
            'owners': [{
                'type': 'team',
                'identifier': 'frontend',
            }]
        }]
    }
Exemple #31
0
    def setUp(self):
        from sentry.ownership.grammar import Rule
        self.user = self.create_user(email='*****@*****.**', is_active=True)
        self.user2 = self.create_user(email='*****@*****.**', is_active=True)

        self.organization = self.create_organization(owner=self.user)
        self.team = self.create_team(organization=self.organization)

        self.project = self.create_project(name='Test', teams=[self.team])
        OrganizationMemberTeam.objects.create(
            organizationmember=OrganizationMember.objects.get(
                user=self.user,
                organization=self.organization,
            ),
            team=self.team,
        )
        self.create_member(user=self.user2,
                           organization=self.organization,
                           teams=[self.team])
        self.group = self.create_group(
            first_seen=timezone.now(),
            last_seen=timezone.now(),
            project=self.project,
            message='hello  world',
            logger='root',
        )
        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema([
                Rule(Matcher('path', '*.py'), [
                    Owner('team', self.team.slug),
                ]),
                Rule(Matcher('path', '*.jx'), [
                    Owner('user', self.user2.email),
                ]),
                Rule(Matcher('path', '*.cbl'), [
                    Owner('user', self.user.email),
                    Owner('user', self.user2.email),
                ])
            ]),
            fallthrough=True,
        )
Exemple #32
0
 def test_team_without_members(self):
     team = self.create_team()
     project = self.create_project(teams=[team], fire_project_created=True)
     ProjectOwnership.objects.create(
         project_id=project.id,
         schema=dump_schema([Rule(Matcher("path", "*.cpp"), [Owner("team", team.slug)])]),
         fallthrough=True,
     )
     rule = project.rule_set.all()[0]
     records = [
         event_to_record(event, (rule,))
         for event in self.create_events_from_filenames(
             project, ["hello.py", "goodbye.py", "hola.py", "adios.py"]
         )
     ]
     digest = build_digest(project, sort_records(records))
     user_ids = [member.user_id for member in team.member_set]
     assert not user_ids
     for user_id, user_digest in get_personalized_digests(project.id, digest, user_ids):
         assert False  # no users in this team no digests should be processed
Exemple #33
0
    def make_ownership(self):
        rule_a = Rule(
            Matcher('path', 'src/*'), [
                Owner('user', self.user.email),
            ])
        rule_b = Rule(
            Matcher('path', 'tests/*'), [
                Owner('team', self.team.name),
            ])
        rule_c = Rule(
            Matcher('path', 'src/app/*'), [
                Owner('team', self.team.name),
            ])

        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema([rule_a, rule_b, rule_c]),
            fallthrough=True,
            auto_assignment=True,
        )
Exemple #34
0
    def setUp(self):
        from sentry.ownership.grammar import Rule

        self.user = self.create_user(email="*****@*****.**", is_active=True)
        self.user2 = self.create_user(email="*****@*****.**", is_active=True)

        self.organization = self.create_organization(owner=self.user)
        self.team = self.create_team(organization=self.organization)

        self.project = self.create_project(name="Test", teams=[self.team])
        OrganizationMemberTeam.objects.create(
            organizationmember=OrganizationMember.objects.get(
                user=self.user, organization=self.organization),
            team=self.team,
        )
        self.create_member(user=self.user2,
                           organization=self.organization,
                           teams=[self.team])
        self.group = self.create_group(
            first_seen=timezone.now(),
            last_seen=timezone.now(),
            project=self.project,
            message="hello  world",
            logger="root",
        )
        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema([
                Rule(Matcher("path", "*.py"), [Owner("team", self.team.slug)]),
                Rule(Matcher("path", "*.jx"),
                     [Owner("user", self.user2.email)]),
                Rule(
                    Matcher("path", "*.cbl"),
                    [
                        Owner("user", self.user.email),
                        Owner("user", self.user2.email)
                    ],
                ),
            ]),
            fallthrough=True,
        )
Exemple #35
0
    def setUp(self):
        from sentry.ownership.grammar import Rule
        self.user = self.create_user(email='*****@*****.**', is_active=True)
        self.user2 = self.create_user(email='*****@*****.**', is_active=True)

        self.organization = self.create_organization(owner=self.user)
        self.team = self.create_team(organization=self.organization)

        self.project = self.create_project(name='Test', teams=[self.team])
        OrganizationMemberTeam.objects.create(
            organizationmember=OrganizationMember.objects.get(
                user=self.user,
                organization=self.organization,
            ),
            team=self.team,
        )
        self.create_member(user=self.user2, organization=self.organization, teams=[self.team])
        self.group = self.create_group(
            first_seen=timezone.now(),
            last_seen=timezone.now(),
            project=self.project,
            message='hello  world',
            logger='root',
        )
        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema([
                Rule(Matcher('path', '*.py'), [
                    Owner('team', self.team.slug),
                ]),
                Rule(Matcher('path', '*.jx'), [
                    Owner('user', self.user2.email),
                ]),
                Rule(Matcher('path', '*.cbl'), [
                    Owner('user', self.user.email),
                    Owner('user', self.user2.email),
                ])
            ]),
            fallthrough=True,
        )
Exemple #36
0
 def test_team_without_members(self):
     team = self.create_team()
     project = self.create_project(teams=[team])
     ProjectOwnership.objects.create(
         project_id=project.id,
         schema=dump_schema([
             Rule(Matcher('path', '*.cpp'), [
                 Owner('team', team.slug),
             ]),
         ]),
         fallthrough=True,
     )
     rule = project.rule_set.all()[0]
     records = [
         event_to_record(event, (rule, )) for event in self.create_events(timezone.now(), project, [
             'hello.py', 'goodbye.py', 'hola.py', 'adios.py'])
     ]
     digest = build_digest(project, sort_records(records))
     user_ids = [member.user_id for member in team.member_set]
     assert not user_ids
     for user_id, user_digest in get_personalized_digests(project.id, digest, user_ids):
         assert False  # no users in this team no digests should be processed
    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)
Exemple #38
0
    def setUp(self):
        self.user1 = self.create_user()
        self.user2 = self.create_user()
        self.user3 = self.create_user()
        self.user4 = self.create_user()
        self.user5 = self.create_user()  # this user has no events
        self.user_ids = [self.user1.id, self.user2.id, self.user3.id, self.user4.id, self.user5.id]

        self.team1 = self.create_team()
        self.team2 = self.create_team()
        self.team3 = self.create_team()

        self.project = self.create_project(teams=[self.team1, self.team2, self.team3])

        self.create_member(user=self.user1, organization=self.organization, teams=[self.team1])
        self.create_member(user=self.user2, organization=self.organization, teams=[self.team2])
        self.create_member(
            user=self.user3,
            organization=self.organization,
            teams=[
                self.team1,
                self.team2])
        self.create_member(user=self.user4, organization=self.organization, teams=[self.team3])
        self.create_member(user=self.user5, organization=self.organization, teams=[self.team3])

        start_time = timezone.now()

        self.team1_events = self.create_events(
            start_time, self.project, [
                'hello.py', 'goodbye.py', 'hola.py', 'adios.py'])
        self.team2_events = self.create_events(
            start_time, self.project, [
                'old.cbl', 'retro.cbl', 'cool.cbl', 'gem.cbl'])

        self.user4_events = [
            self.create_event(
                group=self.create_group(
                    project=self.project), data=self.create_event_data(
                    'foo.bar', 'helloworld.org')),
            self.create_event(
                group=self.create_group(
                    project=self.project), data=self.create_event_data(
                    'bar.foo', 'helloworld.org')),
        ]
        self.team1_matcher = Matcher('path', '*.py')
        self.team2_matcher = Matcher('path', '*.cbl')
        self.user4_matcher = Matcher('url', '*.org')

        ProjectOwnership.objects.create(
            project_id=self.project.id,
            schema=dump_schema([
                Rule(self.team1_matcher, [
                    Owner('team', self.team1.slug),
                    Owner('user', self.user3.email),
                ]),
                Rule(self.team2_matcher, [
                    Owner('team', self.team2.slug),
                ]),
                Rule(self.user4_matcher, [
                    Owner('user', self.user4.email),
                ]),
            ]),
            fallthrough=True,
        )