예제 #1
0
    def test_user_feedback(self):
        user_foo = self.create_user('*****@*****.**')

        report = UserReport.objects.create(project=self.project,
                                           group=self.group,
                                           name='Homer Simpson',
                                           email='*****@*****.**')

        self.project.teams.first().organization.member_set.create(
            user=user_foo)

        with self.tasks():
            self.plugin.handle_signal(
                name='user-reports.created',
                project=self.project,
                payload={
                    'report':
                    serialize(report, AnonymousUser(),
                              ProjectUserReportSerializer()),
                },
            )

        assert len(mail.outbox) == 1

        msg = mail.outbox[0]

        assert msg.subject == '[Sentry] {} - New Feedback from Homer Simpson'.format(
            self.group.qualified_short_id, )
        assert msg.to == [self.user.email]
예제 #2
0
    def get(self, request, project):
        """
        List a Project's User Feedback
        ``````````````````````````````

        Return a list of user feedback items within this project.

        :pparam string organization_slug: the slug of the organization.
        :pparam string project_slug: the slug of the project.
        :auth: required
        """
        queryset = UserReport.objects.filter(
            project=project,
            group__isnull=False,
        ).select_related('group')

        status = request.GET.get('status', 'unresolved')
        if status == 'unresolved':
            queryset = queryset.filter(
                group__status=GroupStatus.UNRESOLVED,
            )
        elif status:
            return Response({'status': 'Invalid status choice'}, status=400)

        return self.paginate(
            request=request,
            queryset=queryset,
            order_by='-date_added',
            on_results=lambda x: serialize(x, request.user, ProjectUserReportSerializer()),
            paginator_cls=DateTimePaginator,
        )
예제 #3
0
    def post(self, request, project):
        """
        Submit User Feedback
        ````````````````````

        Submit and associate user feedback with an issue.

        :pparam string organization_slug: the slug of the organization.
        :pparam string project_slug: the slug of the project.
        :auth: required
        :param string event_id: the event ID
        :param string name: user's name
        :param string email: user's email address
        :param string comments: comments supplied by user
        """
        serializer = UserReportSerializer(data=request.DATA)
        if not serializer.is_valid():
            return Response(serializer.errors, status=400)

        report = serializer.object
        report.project = project
        try:
            mapping = EventMapping.objects.get(
                event_id=report.event_id,
                project_id=project.id,
            )
        except EventMapping.DoesNotExist:
            # XXX(dcramer): the system should fill this in later
            pass
        else:
            report.group = Group.objects.get(id=mapping.group_id)

        try:
            with transaction.atomic():
                report.save()
        except IntegrityError:
            # There was a duplicate, so just overwrite the existing
            # row with the new one. The only way this ever happens is
            # if someone is messing around with the API, or doing
            # something wrong with the SDK, but this behavior is
            # more reasonable than just hard erroring and is more
            # expected.
            report = UserReport.objects.get(
                project=report.project,
                event_id=report.event_id,
            )
            report.update(
                name=report.name,
                email=report.email,
                comments=report.comments,
                date_added=timezone.now(),
            )

        return Response(
            serialize(report, request.user, ProjectUserReportSerializer()))
    def notify(self):
        from django.contrib.auth.models import AnonymousUser
        from sentry.api.serializers import (
            serialize, ProjectUserReportSerializer
        )
        from sentry.tasks.signals import signal

        signal.delay(
            name='user-reports.created',
            project_id=self.project_id,
            payload={
                'report': serialize(self, AnonymousUser(), ProjectUserReportSerializer()),
            },
        )
예제 #5
0
    def get(self, request, project):
        queryset = UserReport.objects.filter(
            project=project,
            group__isnull=False,
        ).select_related('group')

        return self.paginate(
            request=request,
            queryset=queryset,
            order_by='-date_added',
            on_results=lambda x: serialize(x, request.user,
                                           ProjectUserReportSerializer()),
            paginator_cls=DateTimePaginator,
        )
예제 #6
0
    def get(self, request, project):
        """
        List a Project's User Feedback
        ``````````````````````````````

        Return a list of user feedback items within this project.

        :pparam string organization_slug: the slug of the organization.
        :pparam string project_slug: the slug of the project.
        :auth: required
        """
        # we dont allow read permission with DSNs
        if isinstance(request.auth, ProjectKey):
            return self.respond(status=401)

        try:
            environment = self._get_environment_from_request(
                request,
                project.organization_id,
            )
        except Environment.DoesNotExist:
            queryset = UserReport.objects.none()
        else:
            queryset = UserReport.objects.filter(
                project=project,
                group__isnull=False,
            ).select_related('group')
            if environment is not None:
                queryset = queryset.filter(environment=environment, )

            status = request.GET.get('status', 'unresolved')
            if status == 'unresolved':
                queryset = queryset.filter(
                    group__status=GroupStatus.UNRESOLVED, )
            elif status:
                return self.respond({'status': 'Invalid status choice'},
                                    status=400)

        return self.paginate(
            request=request,
            queryset=queryset,
            order_by='-date_added',
            on_results=lambda x: serialize(
                x, request.user,
                ProjectUserReportSerializer(
                    environment_func=self._get_environment_func(
                        request, project.organization_id))),
            paginator_cls=DateTimePaginator,
        )
예제 #7
0
    def get(self, request, project):
        """
        List a Project's User Feedback
        ``````````````````````````````

        Return a list of user feedback items within this project.

        :pparam string organization_slug: the slug of the organization.
        :pparam string project_slug: the slug of the project.
        :auth: required
        """
        queryset = UserReport.objects.filter(
            project=project,
            group__isnull=False,
        ).select_related('group')

        return self.paginate(
            request=request,
            queryset=queryset,
            order_by='-date_added',
            on_results=lambda x: serialize(x, request.user,
                                           ProjectUserReportSerializer()),
            paginator_cls=DateTimePaginator,
        )
예제 #8
0
    def post(self, request, project):
        """
        Submit User Feedback
        ````````````````````

        Submit and associate user feedback with an issue.

        :pparam string organization_slug: the slug of the organization.
        :pparam string project_slug: the slug of the project.
        :auth: required
        :param string event_id: the event ID
        :param string name: user's name
        :param string email: user's email address
        :param string comments: comments supplied by user
        """
        serializer = UserReportSerializer(data=request.DATA)
        if not serializer.is_valid():
            return Response(serializer.errors, status=400)

        report = serializer.object
        report.project = project

        # TODO(dcramer): we should probably create the user if they dont
        # exist, and ideally we'd also associate that with the event
        euser = self.find_event_user(report)
        if euser and not euser.name and report.name:
            euser.update(name=report.name)
        if euser:
            report.event_user_id = euser.id

        try:
            report.group = Group.objects.from_event_id(project,
                                                       report.event_id)
        except Group.DoesNotExist:
            pass

        try:
            with transaction.atomic():
                report.save()
        except IntegrityError:
            # There was a duplicate, so just overwrite the existing
            # row with the new one. The only way this ever happens is
            # if someone is messing around with the API, or doing
            # something wrong with the SDK, but this behavior is
            # more reasonable than just hard erroring and is more
            # expected.
            existing_report = UserReport.objects.get(
                project=report.project,
                event_id=report.event_id,
            )

            existing_report.update(
                name=report.name,
                email=report.email,
                comments=report.comments,
                date_added=timezone.now(),
                event_user_id=euser.id if euser else None,
            )
            report = existing_report

        if report.group_id is None:
            backfill_group.apply_async(
                kwargs={
                    'report_id': report.id,
                },
                countdown=30,
            )

        user_feedback_received.send(project=report.project,
                                    group=report.group,
                                    sender=self)

        return Response(
            serialize(report, request.user, ProjectUserReportSerializer()))
예제 #9
0
    def post(self, request, project):
        """
        Submit User Feedback
        ````````````````````

        Submit and associate user feedback with an issue.

        Feedback must be received by the server no more than 30 minutes after the event was saved.

        Additionally, within 5 minutes of submitting feedback it may also be overwritten. This is useful
        in situations where you may need to retry sending a request due to network failures.

        If feedback is rejected due to a mutability threshold, a 409 status code will be returned.

        :pparam string organization_slug: the slug of the organization.
        :pparam string project_slug: the slug of the project.
        :auth: required
        :param string event_id: the event ID
        :param string name: user's name
        :param string email: user's email address
        :param string comments: comments supplied by user
        """
        serializer = UserReportSerializer(data=request.DATA)
        if not serializer.is_valid():
            return self.respond(serializer.errors, status=400)

        report = serializer.object
        report.project = project

        # TODO(dcramer): we should probably create the user if they dont
        # exist, and ideally we'd also associate that with the event
        euser = self.find_event_user(report)
        if euser and not euser.name and report.name:
            euser.update(name=report.name)
        if euser:
            report.event_user_id = euser.id

        event = Event.objects.filter(
            project_id=project.id,
            event_id=report.event_id,
        ).first()
        if not event:
            try:
                report.group = Group.objects.from_event_id(
                    project, report.event_id)
            except Group.DoesNotExist:
                pass
        else:
            # if the event is more than 30 minutes old, we dont allow updates
            # as it might be abusive
            if event.datetime < timezone.now() - timedelta(minutes=30):
                return self.respond(
                    {'detail': 'Feedback for this event cannot be modified.'},
                    status=409)

            report.environment = event.get_environment()
            report.group = event.group

        try:
            with transaction.atomic():
                report.save()
        except IntegrityError:
            # There was a duplicate, so just overwrite the existing
            # row with the new one. The only way this ever happens is
            # if someone is messing around with the API, or doing
            # something wrong with the SDK, but this behavior is
            # more reasonable than just hard erroring and is more
            # expected.
            existing_report = UserReport.objects.get(
                project=report.project,
                event_id=report.event_id,
            )

            # if the existing report was submitted more than 5 minutes ago, we dont
            # allow updates as it might be abusive (replay attacks)
            if existing_report.date_added < timezone.now() - timedelta(
                    minutes=5):
                return self.respond(
                    {'detail': 'Feedback for this event cannot be modified.'},
                    status=409)

            existing_report.update(
                name=report.name,
                email=report.email,
                comments=report.comments,
                date_added=timezone.now(),
                event_user_id=euser.id if euser else None,
            )
            report = existing_report

        else:
            if report.group:
                report.notify()

        user_feedback_received.send(project=report.project,
                                    group=report.group,
                                    sender=self)

        return self.respond(
            serialize(
                report, request.user,
                ProjectUserReportSerializer(
                    environment_func=self._get_environment_func(
                        request, project.organization_id))))