Esempio n. 1
0
    def notify_digest(self, project, digest, target_type, target_identifier=None):
        metrics.incr("mail_adapter.notify_digest")
        user_ids = self.get_send_to(project, target_type, target_identifier)[
            ExternalProviders.EMAIL
        ]
        logger.info(
            "mail.adapter.notify_digest",
            extra={
                "project_id": project.id,
                "target_type": target_type.value,
                "target_identifier": target_identifier,
                "user_ids": user_ids,
            },
        )
        for user_id, digest in get_personalized_digests(target_type, project.id, digest, user_ids):
            start, end, counts = get_digest_metadata(digest)

            # If there is only one group in this digest (regardless of how many
            # rules it appears in), we should just render this using the single
            # notification template. If there is more than one record for a group,
            # just choose the most recent one.
            if len(counts) == 1:
                group = next(iter(counts))
                record = max(
                    itertools.chain.from_iterable(
                        groups.get(group, []) for groups in digest.values()
                    ),
                    key=lambda record: record.timestamp,
                )
                notification = Notification(record.value.event, rules=record.value.rules)
                return self.notify(notification, target_type, target_identifier)

            context = {
                "start": start,
                "end": end,
                "project": project,
                "digest": digest,
                "counts": counts,
            }

            headers = {
                "X-Sentry-Project": project.slug,
                "X-SMTPAPI": json.dumps({"category": "digest_email"}),
            }

            group = next(iter(counts))
            subject = self.get_digest_subject(group, counts, start)

            self.add_unsubscribe_link(context, user_id, project, "alert_digest")
            self._send_mail(
                subject=subject,
                template="sentry/emails/digests/body.txt",
                html_template="sentry/emails/digests/body.html",
                project=project,
                reference=project,
                headers=headers,
                type="notify.digest",
                context=context,
                send_to=[user_id],
            )
Esempio n. 2
0
    def notify_digest(self, project, digest):
        start, end, counts = get_digest_metadata(digest)

        # If there is only one group in this digest (regardless of how many
        # rules it appears in), we should just render this using the single
        # notification template. If there is more than one record for a group,
        # just choose the most recent one.
        if len(counts) == 1:
            group = counts.keys()[0]
            record = max(
                itertools.chain.from_iterable(
                    groups.get(group, []) for groups in digest.itervalues(),
                ),
                key=lambda record: record.timestamp,
            )
            notification = Notification(record.value.event, rules=record.value.rules)
            return self.notify(notification)

        context = {
            'start': start,
            'end': end,
            'project': project,
            'digest': digest,
            'counts': counts,
        }

        self._send_mail(
            subject=render_to_string('sentry/emails/digests/subject.txt', context).rstrip(),
            template='sentry/emails/digests/body.txt',
            html_template='sentry/emails/digests/body.html',
            project=project,
            context=context,
        )
Esempio n. 3
0
    def rule_notify(
        self,
        event: Any,
        futures: Sequence[Any],
        target_type: ActionTargetType,
        target_identifier: Optional[int] = None,
    ) -> None:
        metrics.incr("mail_adapter.rule_notify")
        rules = []
        extra = {
            "event_id": event.event_id,
            "group_id": event.group_id,
            "is_from_mail_action_adapter": True,
            "target_type": target_type.value,
            "target_identifier": target_identifier,
        }
        log_event = "dispatched"
        for future in futures:
            rules.append(future.rule)
            extra["rule_id"] = future.rule.id
            if not future.kwargs:
                continue
            raise NotImplementedError(
                "The default behavior for notification de-duplication does not support args"
            )

        project = event.group.project
        extra["project_id"] = project.id

        if digests.enabled(project):

            def get_digest_option(key):
                return ProjectOption.objects.get_value(
                    project, get_digest_option_key("mail", key))

            digest_key = unsplit_key(event.group.project, target_type,
                                     target_identifier)
            extra["digest_key"] = digest_key
            immediate_delivery = digests.add(
                digest_key,
                event_to_record(event, rules),
                increment_delay=get_digest_option("increment_delay"),
                maximum_delay=get_digest_option("maximum_delay"),
            )
            if immediate_delivery:
                deliver_digest.delay(digest_key)
            else:
                log_event = "digested"

        else:
            notification = Notification(event=event, rules=rules)
            self.notify(notification, target_type, target_identifier)

        logger.info("mail.adapter.notification.%s" % log_event, extra=extra)
Esempio n. 4
0
    def notify_digest(self,
                      project,
                      digest,
                      target_type,
                      target_identifier=None):
        user_ids = self.get_send_to(project, target_type, target_identifier)
        for user_id, digest in get_personalized_digests(
                project.id, digest, user_ids):
            start, end, counts = get_digest_metadata(digest)

            # If there is only one group in this digest (regardless of how many
            # rules it appears in), we should just render this using the single
            # notification template. If there is more than one record for a group,
            # just choose the most recent one.
            if len(counts) == 1:
                group = six.next(iter(counts))
                record = max(
                    itertools.chain.from_iterable(
                        groups.get(group, [])
                        for groups in six.itervalues(digest)),
                    key=lambda record: record.timestamp,
                )
                notification = Notification(record.value.event,
                                            rules=record.value.rules)
                return self.notify(notification, target_type,
                                   target_identifier)

            context = {
                "start": start,
                "end": end,
                "project": project,
                "digest": digest,
                "counts": counts,
            }

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

            group = six.next(iter(counts))
            subject = self.get_digest_subject(group, counts, start)

            self.add_unsubscribe_link(context, user_id, project,
                                      "alert_digest")
            self._send_mail(
                subject=subject,
                template="sentry/emails/digests/body.txt",
                html_template="sentry/emails/digests/body.html",
                project=project,
                reference=project,
                headers=headers,
                type="notify.digest",
                context=context,
                send_to=[user_id],
            )
Esempio n. 5
0
    def test_notify_failure(self):
        n = NotificationPlugin()
        n.slug = 'slack'

        def hook(*a, **kw):
            raise HTTPError('401 Unauthorized')

        event = self.create_event()
        notification = Notification(event)

        n.notify_users = hook
        assert n.notify(notification) is False
Esempio n. 6
0
    def notify_digest(self, project, digest):
        start, end, counts = get_digest_metadata(digest)

        # If there is only one group in this digest (regardless of how many
        # rules it appears in), we should just render this using the single
        # notification template. If there is more than one record for a group,
        # just choose the most recent one.
        if len(counts) == 1:
            group = six.next(iter(counts))
            record = max(
                itertools.chain.from_iterable(
                    groups.get(group, [])
                    for groups in six.itervalues(digest)),
                key=lambda record: record.timestamp,
            )
            notification = Notification(record.value.event,
                                        rules=record.value.rules)
            return self.notify(notification)

        context = {
            'start': start,
            'end': end,
            'project': project,
            'digest': digest,
            'counts': counts,
        }

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

        group = six.next(iter(counts))
        subject = self.get_digest_subject(group, counts, start)

        for user_id in self.get_send_to(project):
            self.add_unsubscribe_link(context, user_id, project)
            self._send_mail(
                subject=subject,
                template='sentry/emails/digests/body.txt',
                html_template='sentry/emails/digests/body.html',
                project=project,
                reference=project,
                headers=headers,
                type='notify.digest',
                context=context,
                send_to=[user_id],
            )
Esempio n. 7
0
    def test_notify_failure(self):
        errors = (
            ApiError('The server is sad'),
            SSLError('[SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:590)'),
            HTTPError('A bad response'),
        )
        for err in errors:
            n = NotificationPlugin()
            n.slug = 'slack'

            def hook(*a, **kw):
                raise err

            event = self.create_event()
            notification = Notification(event)

            n.notify_users = hook
            assert n.notify(notification) is False
Esempio n. 8
0
    def test_notify_failure(self):
        errors = (
            ApiError("The server is sad"),
            SSLError("[SSL: UNKNOWN_PROTOCOL] unknown protocol (_ssl.c:590)"),
            HTTPError("A bad response"),
            PluginError("A plugin is sad"),
        )
        for err in errors:
            n = NotificationPlugin()
            n.slug = "slack"

            def hook(*a, **kw):
                raise err

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

            n.notify_users = hook
            assert n.notify(notification) is False
Esempio n. 9
0
    def rule_notify(self, event, futures):
        rules = []
        extra = {
            "event_id": event.event_id,
            "group_id": event.group_id,
            "plugin": "mail"
        }
        log_event = "dispatched"
        for future in futures:
            rules.append(future.rule)
            extra["rule_id"] = future.rule.id
            if not future.kwargs:
                continue
            raise NotImplementedError(
                "The default behavior for notification de-duplication does not support args"
            )

        project = event.group.project
        extra["project_id"] = project.id
        if digests.enabled(project):

            def get_digest_option(key):
                return ProjectOption.objects.get_value(
                    project, get_digest_option_key("mail", key))

            digest_key = unsplit_key(self, event.group.project)
            extra["digest_key"] = digest_key
            immediate_delivery = digests.add(
                digest_key,
                event_to_record(event, rules),
                increment_delay=get_digest_option("increment_delay"),
                maximum_delay=get_digest_option("maximum_delay"),
            )
            if immediate_delivery:
                deliver_digest.delay(digest_key)
            else:
                log_event = "digested"

        else:
            notification = Notification(event=event, rules=rules)
            self.notify(notification)

        logger.info("mail.notification.%s" % log_event, extra=extra)
Esempio n. 10
0
    def notify_digest(self, project, digest):
        start, end, counts = get_digest_metadata(digest)

        # If there is only one group in this digest (regardless of how many
        # rules it appears in), we should just render this using the single
        # notification template. If there is more than one record for a group,
        # just choose the most recent one.
        if len(counts) == 1:
            group = counts.keys()[0]
            record = max(
                itertools.chain.from_iterable(
                    groups.get(group, []) for groups in digest.itervalues(),
                ),
                key=lambda record: record.timestamp,
            )
            notification = Notification(record.value.event, rules=record.value.rules)
            return self.notify(notification)

        context = {
            'start': start,
            'end': end,
            'project': project,
            'digest': digest,
            'counts': counts,
        }

        # TODO: Everything below should instead use `_send_mail` for consistency.
        subject_prefix = project.get_option('subject_prefix', settings.EMAIL_SUBJECT_PREFIX)
        if subject_prefix:
            subject_prefix = subject_prefix.rstrip() + ' '

        message = self._build_message(
            subject=subject_prefix + render_to_string('sentry/emails/digests/subject.txt', context).rstrip(),
            template='sentry/emails/digests/body.txt',
            html_template='sentry/emails/digests/body.html',
            project=project,
            context=context,
        )

        if message is not None:
            message.send()