Exemplo n.º 1
0
 def run_test(
     self,
     activity,
     expected_username,
     expected_action,
     expected_comment,
     expected_recipient,
 ):
     incident = activity.incident
     context = build_activity_context(activity, expected_recipient)
     assert context['user_name'] == expected_username
     assert context['action'] == '%s on incident %s (#%s)' % (
         expected_action,
         activity.incident.title,
         activity.incident.identifier,
     )
     assert context['link'] == absolute_uri(
         reverse(
             'sentry-incident',
             kwargs={
                 'organization_slug': incident.organization.slug,
                 'incident_id': incident.identifier,
             },
         )) + '?referrer=incident_activity_email'
     assert context['comment'] == expected_comment
     assert context['unsubscribe_link'] == generate_signed_link(
         expected_recipient,
         'sentry-account-email-unsubscribe-incident',
         kwargs={'incident_id': incident.id},
     )
Exemplo n.º 2
0
def build_activity_context(activity, user):
    if activity.type == IncidentActivityType.COMMENT.value:
        action = "left a comment"
    else:
        action = "changed status from %s to %s" % (
            IncidentStatus(int(activity.previous_value)).name.lower(),
            IncidentStatus(int(activity.value)).name.lower(),
        )
    incident = activity.incident

    action = "%s on incident %s (#%s)" % (action, incident.title,
                                          incident.identifier)

    return {
        "user_name":
        activity.user.name if activity.user else "Sentry",
        "action":
        action,
        "link":
        absolute_uri(
            reverse(
                "sentry-incident",
                kwargs={
                    "organization_slug": incident.organization.slug,
                    "incident_id": incident.identifier,
                },
            )) + "?" + urlencode({"referrer": "incident_activity_email"}),
        "comment":
        activity.comment,
        "unsubscribe_link":
        generate_signed_link(user,
                             "sentry-account-email-unsubscribe-incident",
                             kwargs={"incident_id": incident.id}),
    }
Exemplo n.º 3
0
 def run_test(
     self, activity, expected_username, expected_action, expected_comment, expected_recipient
 ):
     incident = activity.incident
     context = build_activity_context(activity, expected_recipient)
     assert context["user_name"] == expected_username
     assert context["action"] == "%s on incident %s (#%s)" % (
         expected_action,
         activity.incident.title,
         activity.incident.identifier,
     )
     assert (
         context["link"]
         == absolute_uri(
             reverse(
                 "sentry-incident",
                 kwargs={
                     "organization_slug": incident.organization.slug,
                     "incident_id": incident.identifier,
                 },
             )
         )
         + "?referrer=incident_activity_email"
     )
     assert context["comment"] == expected_comment
     assert context["unsubscribe_link"] == generate_signed_link(
         expected_recipient,
         "sentry-account-email-unsubscribe-incident",
         kwargs={"incident_id": incident.id},
     )
    def test_process(self):
        instance = self.create_instance()
        path = generate_signed_link(user=self.user, viewname=self.view_name, args=[instance.id])

        resp = self.client.post(path, data={"op": "unsubscribe"})
        assert resp.status_code == 302
        self.assert_unsubscribed(instance, self.user)
    def test_no_access(self):
        user = self.create_user("*****@*****.**")
        instance = self.create_instance()
        path = generate_signed_link(user=user, viewname=self.view_name, args=[instance.id])

        resp = self.client.get(path)
        assert resp.status_code == 404
Exemplo n.º 6
0
def build_activity_context(activity, user):
    if activity.type == IncidentActivityType.COMMENT.value:
        action = 'left a comment'
    else:
        action = 'changed status from %s to %s' % (
            IncidentStatus(int(activity.previous_value)).name.lower(),
            IncidentStatus(int(activity.value)).name.lower(),
        )
    incident = activity.incident

    action = '%s on incident %s (#%s)' % (action, incident.title, incident.identifier)

    return {
        'user_name': activity.user.name if activity.user else 'Sentry',
        'action': action,
        'link': absolute_uri(reverse(
            'sentry-incident',
            kwargs={
                'organization_slug': incident.organization.slug,
                'incident_id': incident.identifier,
            },
        )) + '?' + urlencode({'referrer': 'incident_activity_email'}),
        'comment': activity.comment,
        'unsubscribe_link': generate_signed_link(
            user,
            'sentry-account-email-unsubscribe-incident',
            kwargs={'incident_id': incident.id},
        ),
    }
    def test_invalid_issue(self):
        path = generate_signed_link(user=self.user,
                                    viewname=self.view_name,
                                    args=[13413434])

        resp = self.client.get(path)
        assert resp.status_code == 404
Exemplo n.º 8
0
 def add_unsubscribe_link(self, context, user_id, project, referrer):
     context["unsubscribe_link"] = generate_signed_link(
         user_id,
         "sentry-account-email-unsubscribe-project",
         referrer,
         kwargs={"project_id": project.id},
     )
Exemplo n.º 9
0
 def add_unsubscribe_link(self, context, user_id, project):
     context['unsubscribe_link'] = generate_signed_link(
         user_id,
         'sentry-account-email-unsubscribe-project',
         kwargs={
             'project_id': project.id,
         })
Exemplo n.º 10
0
    def test_sso_auth_required_signed_link(self):
        user = self.create_user("*****@*****.**", is_superuser=False)
        organization = self.create_organization(name="foo")
        team = self.create_team(name="bar", organization=organization)
        project = self.create_project(name="baz", organization=organization, teams=[team])
        member = self.create_member(user=user, organization=organization, teams=[team])
        setattr(member.flags, "sso:linked", True)
        member.save()

        self.store_event(data={}, project_id=project.id)

        auth_provider = AuthProvider.objects.create(
            organization=organization, provider="dummy", flags=0
        )

        AuthIdentity.objects.create(auth_provider=auth_provider, user=user)

        self.login_as(user)

        unsigned_link = reverse(
            "sentry-api-0-project-fix-processing-issues",
            kwargs={"project_slug": project.slug, "organization_slug": organization.slug},
        )

        resp = self.client.get(unsigned_link)
        assert resp.status_code == 401, (resp.status_code, resp.content)

        signed_link = generate_signed_link(
            user,
            "sentry-api-0-project-fix-processing-issues",
            kwargs={"project_slug": project.slug, "organization_slug": organization.slug},
        )

        resp = self.client.get(signed_link)
        assert resp.status_code == 200
Exemplo n.º 11
0
 def add_unsubscribe_link(self, context, user_id, project):
     context['unsubscribe_link'] = generate_signed_link(
         user_id,
         'sentry-account-email-unsubscribe-project',
         kwargs={
             'project_id': project.id,
         }
     )
Exemplo n.º 12
0
    def test_renders(self):
        instance = self.create_instance()
        path = generate_signed_link(user=self.user,
                                    viewname=self.view_name,
                                    args=[instance.id])

        resp = self.client.get(path)
        assert resp.status_code == 200
Exemplo n.º 13
0
def get_unsubscribe_link(user_id: int,
                         resource_id: int,
                         key: str = "issue",
                         referrer: Optional[str] = None) -> str:
    return generate_signed_link(
        user_id,
        f"sentry-account-email-unsubscribe-{key}",
        referrer,
        kwargs={f"{key}_id": resource_id},
    )
Exemplo n.º 14
0
    def send(self):
        if not self.should_email():
            return

        participants = self.get_participants()
        if not participants:
            return

        activity = self.activity
        project = self.project
        group = self.group

        context = self.get_base_context()
        context.update(self.get_context())

        template = self.get_template()
        html_template = self.get_html_template()
        email_type = self.get_email_type()
        headers = self.get_headers()

        for user, reason in participants.items():
            if group:
                context.update(
                    {
                        'reason':
                        GroupSubscriptionReason.descriptions.get(
                            reason,
                            "are subscribed to this issue",
                        ),
                        'unsubscribe_link':
                        generate_signed_link(
                            user.id,
                            'sentry-account-email-unsubscribe-issue',
                            kwargs={'issue_id': group.id},
                        ),
                    }
                )
            user_context = self.get_user_context(user)
            if user_context:
                user_context.update(context)
            else:
                user_context = context

            msg = MessageBuilder(
                subject=self.get_subject_with_prefix(),
                template=template,
                html_template=html_template,
                headers=headers,
                type=email_type,
                context=user_context,
                reference=activity,
                reply_reference=group,
            )
            msg.add_users([user.id], project=project)
            msg.send_async()
Exemplo n.º 15
0
    def test_invalid_issue(self):

        path = generate_signed_link(
            user=self.user,
            viewname='sentry-account-email-unsubscribe-issue',
            args=[13413434],
        )

        resp = self.client.get(path)

        assert resp.status_code == 404
Exemplo n.º 16
0
    def test_renders(self):
        group = self.create_group()

        path = generate_signed_link(
            user=self.user,
            viewname='sentry-account-email-unsubscribe-issue',
            args=[group.id],
        )

        resp = self.client.get(path)

        assert resp.status_code == 200
Exemplo n.º 17
0
    def test_no_access(self):
        user = self.create_user('*****@*****.**')
        group = self.create_group()

        path = generate_signed_link(
            user=user,
            viewname='sentry-account-email-unsubscribe-issue',
            args=[group.id],
        )

        resp = self.client.get(path)

        assert resp.status_code == 404
Exemplo n.º 18
0
    def test_sso_auth_required_signed_link(self):
        user = self.create_user('*****@*****.**', is_superuser=False)
        organization = self.create_organization(name='foo')
        team = self.create_team(name='bar', organization=organization)
        project = self.create_project(
            name='baz', organization=organization, teams=[team])
        member = self.create_member(
            user=user, organization=organization, teams=[team])
        setattr(member.flags, 'sso:linked', True)
        member.save()

        self.store_event(
            data={},
            project_id=project.id,
        )

        auth_provider = AuthProvider.objects.create(
            organization=organization,
            provider='dummy',
            flags=0,
        )

        AuthIdentity.objects.create(
            auth_provider=auth_provider,
            user=user,
        )

        self.login_as(user)

        unsigned_link = reverse(
            'sentry-api-0-project-fix-processing-issues',
            kwargs={
                'project_slug': project.slug,
                'organization_slug': organization.slug,
            }
        )

        resp = self.client.get(unsigned_link)
        assert resp.status_code == 401, (resp.status_code, resp.content)

        signed_link = generate_signed_link(
            user,
            'sentry-api-0-project-fix-processing-issues',
            kwargs={
                'project_slug': project.slug,
                'organization_slug': organization.slug,
            }
        )

        resp = self.client.get(signed_link)
        assert resp.status_code == 200
Exemplo n.º 19
0
    def test_sso_auth_required_signed_link(self):
        user = self.create_user('*****@*****.**', is_superuser=False)
        organization = self.create_organization(name='foo')
        team = self.create_team(name='bar', organization=organization)
        project = self.create_project(
            name='baz', organization=organization, team=team)
        member = self.create_member(
            user=user, organization=organization, teams=[team])
        setattr(member.flags, 'sso:linked', True)
        member.save()
        group = self.create_group(project=project)
        self.create_event(group=group)

        auth_provider = AuthProvider.objects.create(
            organization=organization,
            provider='dummy',
            flags=0,
        )

        AuthIdentity.objects.create(
            auth_provider=auth_provider,
            user=user,
        )

        self.login_as(user)

        unsigned_link = reverse(
            'sentry-api-0-project-fix-processing-issues',
            kwargs={
                'project_slug': project.slug,
                'organization_slug': organization.slug,
            }
        )

        resp = self.client.get(unsigned_link)
        assert resp.status_code == 401, (resp.status_code, resp.content)

        signed_link = generate_signed_link(
            user,
            'sentry-api-0-project-fix-processing-issues',
            kwargs={
                'project_slug': project.slug,
                'organization_slug': organization.slug,
            }
        )

        resp = self.client.get(signed_link)
        assert resp.status_code == 200
Exemplo n.º 20
0
    def get(self, request, project):
        """
        List a project's processing issues.
        """
        num_issues = ProcessingIssue.objects.filter(
            project=project
        ).count()

        last_seen = ProcessingIssue.objects.filter(
            project=project
        ).order_by('-datetime').first()

        resolveable_issues, has_more = ProcessingIssue.objects \
            .find_resolved(project_id=project.id)

        reprocessing_issues = ReprocessingReport.objects \
            .filter(project_id=project.id).count()

        signed_link = None
        if num_issues > 0:
            signed_link = generate_signed_link(
                request.user,
                'sentry-api-0-project-fix-processing-issues',
                kwargs={
                    'project_slug': project.slug,
                    'organization_slug': project.organization.slug,
                }
            )

        data = {
            'hasIssues': num_issues > 0,
            'numIssues': num_issues,
            'lastSeen': last_seen and serialize(last_seen.datetime) or None,
            'resolveableIssues': len(resolveable_issues),
            'hasMoreResolveableIssues': has_more,
            'issuesProcessing': reprocessing_issues,
            'signedLink': signed_link
        }

        if request.GET.get('detailed') == '1':
            q = ProcessingIssue.objects.with_num_events().filter(
                project=project
            ).order_by('type', 'datetime')
            data['issues'] = [serialize(x, request.user) for x in q]

        return Response(serialize(data, request.user))
Exemplo n.º 21
0
    def send(self):
        if not self.should_email():
            return

        users = self.get_participants()
        if not users:
            return

        activity = self.activity
        project = self.project
        group = self.group

        context = self.get_base_context()
        context.update(self.get_context())

        subject_prefix = self._get_subject_prefix()

        subject = (u'{}{}'.format(
            subject_prefix,
            self.get_subject(),
        )).encode('utf-8')
        template = self.get_template()
        html_template = self.get_html_template()
        email_type = self.get_email_type()
        headers = self.get_headers()

        for user in users:
            if group:
                context['unsubscribe_link'] = generate_signed_link(
                    user.id,
                    'sentry-account-email-unsubscribe-issue',
                    kwargs={'issue_id': group.id},
                )

            msg = MessageBuilder(
                subject=subject,
                template=template,
                html_template=html_template,
                headers=headers,
                type=email_type,
                context=context,
                reference=activity,
                reply_reference=group,
            )
            msg.add_users([user.id], project=project)
            msg.send_async()
Exemplo n.º 22
0
    def test_sso_auth_required_signed_link(self):
        unsigned_link = reverse(
            "sentry-api-0-project-fix-processing-issues",
            kwargs={"project_slug": self.project.slug, "organization_slug": self.organization.slug},
        )

        resp = self.client.get(unsigned_link)
        assert resp.status_code == 401, (resp.status_code, resp.content)

        signed_link = generate_signed_link(
            self.user,
            "sentry-api-0-project-fix-processing-issues",
            kwargs={"project_slug": self.project.slug, "organization_slug": self.organization.slug},
        )

        resp = self.client.get(signed_link)
        assert resp.status_code == 200
Exemplo n.º 23
0
    def test_process(self):
        group = self.create_group()

        path = generate_signed_link(
            user=self.user,
            viewname='sentry-account-email-unsubscribe-issue',
            args=[group.id],
        )

        resp = self.client.post(path, data={'op': 'unsubscribe'})

        assert resp.status_code == 302
        assert GroupSubscription.objects.filter(
            user=self.user,
            group=group,
            is_active=False,
        ).exists()
Exemplo n.º 24
0
    def test_link_signing(self):
        rf = RequestFactory()

        url = linksign.generate_signed_link(self.user, "sentry")
        assert url.startswith("http://")

        req = rf.get("/" + url.split("/", 3)[-1])
        signed_user = linksign.process_signature(req)
        assert signed_user
        assert signed_user.id == self.user.id

        req = rf.get("/what" + url.split("/", 3)[-1])
        signed_user = linksign.process_signature(req)
        assert signed_user is None

        req = rf.get("/" + url.split("/", 3)[-1] + "garbage")
        signed_user = linksign.process_signature(req)
        assert signed_user is None

        rf.defaults["SERVER_NAME"] = "something-else"
        req = rf.get("/" + url.split("/", 3)[-1])
        signed_user = linksign.process_signature(req)
        assert signed_user is None
Exemplo n.º 25
0
    def test_link_signing(self):
        rf = RequestFactory()

        url = linksign.generate_signed_link(self.user, 'sentry')
        assert url.startswith('http://')

        req = rf.get('/' + url.split('/', 3)[-1])
        signed_user = linksign.process_signature(req)
        assert signed_user
        assert signed_user.id == self.user.id

        req = rf.get('/what' + url.split('/', 3)[-1])
        signed_user = linksign.process_signature(req)
        assert signed_user is None

        req = rf.get('/' + url.split('/', 3)[-1] + 'garbage')
        signed_user = linksign.process_signature(req)
        assert signed_user is None

        rf.defaults['SERVER_NAME'] = 'something-else'
        req = rf.get('/' + url.split('/', 3)[-1])
        signed_user = linksign.process_signature(req)
        assert signed_user is None
    def test_link_signing(self):
        rf = RequestFactory()

        url = linksign.generate_signed_link(self.user, 'sentry')
        assert url.startswith('http://')

        req = rf.get('/' + url.split('/', 3)[-1])
        signed_user = linksign.process_signature(req)
        assert signed_user
        assert signed_user.id == self.user.id

        req = rf.get('/what' + url.split('/', 3)[-1])
        signed_user = linksign.process_signature(req)
        assert signed_user is None

        req = rf.get('/' + url.split('/', 3)[-1] + 'garbage')
        signed_user = linksign.process_signature(req)
        assert signed_user is None

        rf.defaults['SERVER_NAME'] = 'something-else'
        req = rf.get('/' + url.split('/', 3)[-1])
        signed_user = linksign.process_signature(req)
        assert signed_user is None
Exemplo n.º 27
0
    def handle_user_report(self, payload, project, **kwargs):
        metrics.incr("mail_adapter.handle_user_report")
        group = Group.objects.get(id=payload["report"]["issue"]["id"])

        participants = GroupSubscription.objects.get_participants(group=group)

        if not participants:
            return

        org = group.organization
        enhanced_privacy = org.flags.enhanced_privacy

        context = {
            "project":
            project,
            "project_link":
            absolute_uri(u"/{}/{}/".format(project.organization.slug,
                                           project.slug)),
            "issue_link":
            absolute_uri(u"/{}/{}/issues/{}/".format(
                project.organization.slug, project.slug,
                payload["report"]["issue"]["id"])),
            # TODO(dcramer): we dont have permalinks to feedback yet
            "link":
            absolute_uri(u"/{}/{}/issues/{}/feedback/".format(
                project.organization.slug, project.slug,
                payload["report"]["issue"]["id"])),
            "group":
            group,
            "report":
            payload["report"],
            "enhanced_privacy":
            enhanced_privacy,
        }

        subject_prefix = self._build_subject_prefix(project)
        subject = force_text(u"{}{} - New Feedback from {}".format(
            subject_prefix, group.qualified_short_id,
            payload["report"]["name"]))

        headers = {"X-Sentry-Project": project.slug}

        # TODO(dcramer): this is copypasta'd from activity notifications
        # and while it'd be nice to re-use all of that, they are currently
        # coupled to <Activity> instances which makes this tough
        for user, reason in participants.items():
            context.update({
                "reason":
                GroupSubscriptionReason.descriptions.get(
                    reason, "are subscribed to this issue"),
                "unsubscribe_link":
                generate_signed_link(
                    user.id,
                    "sentry-account-email-unsubscribe-issue",
                    kwargs={"issue_id": group.id},
                ),
            })

            msg = MessageBuilder(
                subject=subject,
                template="sentry/emails/activity/new-user-feedback.txt",
                html_template="sentry/emails/activity/new-user-feedback.html",
                headers=headers,
                type="notify.user-report",
                context=context,
                reference=group,
            )
            msg.add_users([user.id], project=project)
            msg.send_async()
Exemplo n.º 28
0
 def generate_unsubscribe_link(self, user_id):
     return generate_signed_link(
         user_id,
         "sentry-account-email-unsubscribe-project",
         kwargs={"project_id": self.project.id},
     )
Exemplo n.º 29
0
    def handle_user_report(self, payload, project, **kwargs):
        from sentry.models import Group, GroupSubscription, GroupSubscriptionReason

        group = Group.objects.get(id=payload['report']['issue']['id'])

        participants = GroupSubscription.objects.get_participants(group=group)

        if not participants:
            return

        context = {
            'project': project,
            'project_link': absolute_uri(u'/{}/{}/'.format(
                project.organization.slug,
                project.slug,
            )),
            'issue_link': absolute_uri(u'/{}/{}/issues/{}/'.format(
                project.organization.slug,
                project.slug,
                payload['report']['issue']['id'],
            )),
            # TODO(dcramer): we dont have permalinks to feedback yet
            'link': absolute_uri(u'/{}/{}/issues/{}/feedback/'.format(
                project.organization.slug,
                project.slug,
                payload['report']['issue']['id'],
            )),
            'group': group,
            'report': payload['report'],
        }

        subject_prefix = self.get_option('subject_prefix', project) or self._subject_prefix()
        subject_prefix = force_text(subject_prefix)
        subject = force_text(u'{}{} - New Feedback from {}'.format(
            subject_prefix,
            group.qualified_short_id,
            payload['report']['name'],
        ))

        headers = {
            'X-Sentry-Project': project.slug,
        }

        # TODO(dcramer): this is copypasta'd from activity notifications
        # and while it'd be nice to re-use all of that, they are currently
        # coupled to <Activity> instances which makes this tough
        for user, reason in participants.items():
            context.update({
                'reason': GroupSubscriptionReason.descriptions.get(
                    reason,
                    "are subscribed to this issue",
                ),
                'unsubscribe_link': generate_signed_link(
                    user.id,
                    'sentry-account-email-unsubscribe-issue',
                    kwargs={'issue_id': group.id},
                ),
            })

            msg = MessageBuilder(
                subject=subject,
                template='sentry/emails/activity/new-user-feedback.txt',
                html_template='sentry/emails/activity/new-user-feedback.html',
                headers=headers,
                type='notify.user-report',
                context=context,
                reference=group,
            )
            msg.add_users([user.id], project=project)
            msg.send_async()
Exemplo n.º 30
0
def get_processing_issues(user, projects, include_detailed_issues=False):
    """
    Given a list of projects, returns a list containing stats about processing
    issues for those projects
    :param include_detailed_issues: Include specific details on each processing
    issue
    :return: A list of dicts, with each dict containing keys:
        - 'hasIssues': Whether the project has any processing issues
        - 'numIssues': How many processing issues the project has
        - 'lastSeen': The date a processing issue was last seen
        - 'resolveableIssues': How many Raw Events have no remaining issues and
        can be resolved automatically
        - 'hasMoreResolveableIssues': Whether there are any Raw Events that
        have no remaining issues and can be resolved automatically
        'issuesProcessing': How many ReprocessingReports exist for this Project
        'signedLink': Signed link that takes the user to the reprocessing page
        for this project
        'project': Slug for the project

    """
    project_agg_results = {
        result["project"]: result
        for result in ProcessingIssue.objects.filter(
            project__in=projects).values("project").annotate(
                num_issues=Count("id"), last_seen=Max("datetime"))
    }
    project_reprocessing_issues = {
        result["project"]: result["reprocessing_issues"]
        for result in ReprocessingReport.objects.filter(
            project__in=projects).values("project").annotate(
                reprocessing_issues=Count("id"))
    }

    resolved_qs = ProcessingIssue.objects.find_resolved_queryset(
        [p.id for p in projects])
    project_resolveable = {
        result["project"]: result["count"]
        for result in resolved_qs.values("project").annotate(count=Count("id"))
    }

    if include_detailed_issues:
        project_issues = defaultdict(list)
        for proc_issue in (ProcessingIssue.objects.with_num_events().filter(
                project__in=projects).order_by("type", "datetime")):
            project_issues[proc_issue.project_id].append(proc_issue)

    project_results = []
    for project in projects:
        agg_results = project_agg_results.get(project.id, {})
        num_issues = agg_results.get("num_issues", 0)

        signed_link = None
        if num_issues > 0:
            signed_link = generate_signed_link(
                user,
                "sentry-api-0-project-fix-processing-issues",
                kwargs={
                    "project_slug": project.slug,
                    "organization_slug": project.organization.slug,
                },
            )

        last_seen = agg_results.get("last_seen")
        data = {
            "hasIssues": num_issues > 0,
            "numIssues": num_issues,
            "lastSeen": last_seen and serialize(last_seen) or None,
            "resolveableIssues": project_resolveable.get(project.id, 0),
            # XXX: Due to a bug in `find_resolved`, this was always returning
            # False. It's unused in our frontend, so just defaulting to False
            # so that we don't break any other consumers that expect this value.
            "hasMoreResolveableIssues": False,
            "issuesProcessing": project_reprocessing_issues.get(project.id, 0),
            "signedLink": signed_link,
            "project": project.slug,
        }
        if include_detailed_issues:
            issues = project_issues[project.id]
            data["issues"] = [serialize(issue, user) for issue in issues]

        project_results.append(data)

    return project_results
Exemplo n.º 31
0
def get_processing_issues(user, projects, include_detailed_issues=False):
    """
    Given a list of projects, returns a list containing stats about processing
    issues for those projects
    :param include_detailed_issues: Include specific details on each processing
    issue
    :return: A list of dicts, with each dict containing keys:
        - 'hasIssues': Whether the project has any processing issues
        - 'numIssues': How many processing issues the project has
        - 'lastSeen': The date a processing issue was last seen
        - 'resolveableIssues': How many Raw Events have no remaining issues and
        can be resolved automatically
        - 'hasMoreResolveableIssues': Whether there are any Raw Events that
        have no remaining issues and can be resolved automatically
        'issuesProcessing': How many ReprocessingReports exist for this Project
        'signedLink': Signed link that takes the user to the reprocessing page
        for this project
        'project': Slug for the project

    """
    project_agg_results = {
        result['project']: result for result in ProcessingIssue.objects.filter(
            project__in=projects,
        ).values('project').annotate(
            num_issues=Count('id'),
            last_seen=Max('datetime'),
        )
    }
    project_reprocessing_issues = {
        result['project']: result['reprocessing_issues'] for result in ReprocessingReport.objects.filter(
            project__in=projects,
        ).values('project').annotate(reprocessing_issues=Count('id'))
    }

    resolved_qs = ProcessingIssue.objects.find_resolved_queryset([p.id for p in projects])
    project_resolveable = {
        result['project']: result['count']
        for result in resolved_qs.values('project').annotate(count=Count('id'))
    }

    if include_detailed_issues:
        project_issues = defaultdict(list)
        for proc_issue in ProcessingIssue.objects.with_num_events().filter(
            project__in=projects,
        ).order_by('type', 'datetime'):
            project_issues[proc_issue.project_id].append(proc_issue)

    project_results = []
    for project in projects:
        agg_results = project_agg_results.get(project.id, {})
        num_issues = agg_results.get('num_issues', 0)

        signed_link = None
        if num_issues > 0:
            signed_link = generate_signed_link(
                user,
                'sentry-api-0-project-fix-processing-issues',
                kwargs={
                    'project_slug': project.slug,
                    'organization_slug': project.organization.slug,
                }
            )

        last_seen = agg_results.get('last_seen')
        data = {
            'hasIssues': num_issues > 0,
            'numIssues': num_issues,
            'lastSeen': last_seen and serialize(last_seen) or None,
            'resolveableIssues': project_resolveable.get(project.id, 0),
            # XXX: Due to a bug in `find_resolved`, this was always returning
            # False. It's unused in our frontend, so just defaulting to False
            # so that we don't break any other consumers that expect this value.
            'hasMoreResolveableIssues': False,
            'issuesProcessing': project_reprocessing_issues.get(project.id, 0),
            'signedLink': signed_link,
            'project': project.slug,
        }
        if include_detailed_issues:
            issues = project_issues[project.id]
            data['issues'] = [serialize(issue, user) for issue in issues]

        project_results.append(data)

    return project_results
Exemplo n.º 32
0
    def handle_user_report(self, payload, project, **kwargs):
        from sentry.models import Group, GroupSubscription, GroupSubscriptionReason

        group = Group.objects.get(id=payload['report']['issue']['id'])

        participants = GroupSubscription.objects.get_participants(group=group)

        if not participants:
            return

        context = {
            'project':
            project,
            'project_link':
            absolute_uri('/{}/{}/'.format(
                project.organization.slug,
                project.slug,
            )),
            'issue_link':
            absolute_uri('/{}/{}/issues/{}/'.format(
                project.organization.slug,
                project.slug,
                payload['report']['issue']['id'],
            )),
            # TODO(dcramer): we dont have permalinks to feedback yet
            'link':
            absolute_uri('/{}/{}/issues/{}/feedback/'.format(
                project.organization.slug,
                project.slug,
                payload['report']['issue']['id'],
            )),
            'group':
            group,
            'report':
            payload['report'],
        }

        subject_prefix = self.get_option('subject_prefix',
                                         project) or self._subject_prefix()
        subject_prefix = force_text(subject_prefix)
        subject = force_text(u'{}{} - New Feedback from {}'.format(
            subject_prefix,
            group.qualified_short_id,
            payload['report']['name'],
        ))

        headers = {
            'X-Sentry-Project': project.slug,
        }

        # TODO(dcramer): this is copypasta'd from activity notifications
        # and while it'd be nice to re-use all of that, they are currently
        # coupled to <Activity> instances which makes this tough
        for user, reason in participants.items():
            context.update({
                'reason':
                GroupSubscriptionReason.descriptions.get(
                    reason,
                    "are subscribed to this issue",
                ),
                'unsubscribe_link':
                generate_signed_link(
                    user.id,
                    'sentry-account-email-unsubscribe-issue',
                    kwargs={'issue_id': group.id},
                ),
            })

            msg = MessageBuilder(
                subject=subject,
                template='sentry/emails/activity/new-user-feedback.txt',
                html_template='sentry/emails/activity/new-user-feedback.html',
                headers=headers,
                type='notify.user-report',
                context=context,
                reference=group,
            )
            msg.add_users([user.id], project=project)
            msg.send_async()
Exemplo n.º 33
0
def get_unsubscribe_link(user_id: int, group_id: int) -> str:
    return generate_signed_link(
        user_id,
        "sentry-account-email-unsubscribe-issue",
        kwargs={"issue_id": group_id},
    )