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], )
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, )
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)
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], )
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
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], )
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
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
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)
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()