Skip to content
This repository was archived by the owner on May 5, 2025. It is now read-only.

Commit 20eb6c5

Browse files
authored
Refactor and simplify notification logic a bit (#1185)
1 parent 6e3bd8b commit 20eb6c5

5 files changed

Lines changed: 77 additions & 125 deletions

File tree

services/notification/__init__.py

Lines changed: 60 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ def get_notifiers_instances(self) -> Iterator[AbstractBaseNotifier]:
162162
if yaml_field is not None:
163163
for instance_type, instance_configs in yaml_field.items():
164164
class_to_use = mapping.get(instance_type)
165+
if not class_to_use:
166+
continue
165167
for title, individual_config in instance_configs.items():
166168
yield class_to_use(
167169
repository=self.repository,
@@ -269,127 +271,107 @@ def notify(self, comparison: ComparisonProxy) -> list[IndividualResult]:
269271
),
270272
)
271273

272-
results = []
273-
status_or_checks_helper_text = {}
274-
notifiers_to_notify = [
274+
status_or_checks_notifiers, all_other_notifiers = split_notifiers(
275275
notifier
276276
for notifier in self.get_notifiers_instances()
277277
if notifier.is_enabled()
278-
]
279-
280-
status_or_checks_notifiers = [
281-
notifier
282-
for notifier in notifiers_to_notify
283-
if notifier.notification_type in notification_type_status_or_checks
284-
]
285-
286-
all_other_notifiers = [
287-
notifier
288-
for notifier in notifiers_to_notify
289-
if notifier.notification_type not in notification_type_status_or_checks
290-
]
278+
)
291279

292-
status_or_checks_results = [
280+
results = [
293281
self.notify_individual_notifier(notifier, comparison)
294282
for notifier in status_or_checks_notifiers
295283
]
296284

297-
if status_or_checks_results and all_other_notifiers:
285+
status_or_checks_helper_text = {}
286+
if results and all_other_notifiers:
298287
# if the status/check fails, sometimes we want to add helper text to the message of the other notifications,
299288
# to better surface that the status/check failed.
300289
# so if there are status_and_checks_notifiers and all_other_notifiers, do the status_and_checks_notifiers first,
301290
# look at the results of the checks, if any failed AND they are the type we have helper text for,
302291
# add that text onto the other notifiers messages through status_or_checks_helper_text.
303-
for result in status_or_checks_results:
304-
notification_result = result["result"]
305-
if (
306-
notification_result is not None
307-
and notification_result.data_sent is not None
308-
):
292+
for _notifier, result in results:
293+
if result is not None and result.data_sent is not None:
309294
if (
310-
notification_result.data_sent.get("state")
311-
== StatusState.failure.value
312-
) and notification_result.data_sent.get("included_helper_text"):
295+
result.data_sent.get("state") == StatusState.failure.value
296+
) and result.data_sent.get("included_helper_text"):
313297
status_or_checks_helper_text.update(
314-
notification_result.data_sent["included_helper_text"]
298+
result.data_sent["included_helper_text"]
315299
)
316-
results = results + status_or_checks_results
317300

318-
results = results + [
301+
results.extend(
319302
self.notify_individual_notifier(
320303
notifier,
321304
comparison,
322305
status_or_checks_helper_text=status_or_checks_helper_text,
323306
)
324307
for notifier in all_other_notifiers
308+
)
309+
310+
return [
311+
IndividualResult(
312+
notifier=notifier.name, title=notifier.title, result=result
313+
)
314+
for notifier, result in results
325315
]
326-
return results
327316

328317
def notify_individual_notifier(
329318
self,
330319
notifier: AbstractBaseNotifier,
331320
comparison: ComparisonProxy,
332321
status_or_checks_helper_text: Optional[dict[str, str]] = None,
333-
) -> IndividualResult:
322+
) -> tuple[AbstractBaseNotifier, NotificationResult | None]:
334323
commit = comparison.head.commit
335324
base_commit = comparison.project_coverage_base.commit
336-
log.info(
337-
"Attempting individual notification",
338-
extra=dict(
339-
commit=commit.commitid,
340-
base_commit=(
341-
base_commit.commitid if base_commit is not None else "NO_BASE"
342-
),
343-
repoid=commit.repoid,
344-
notifier=notifier.name,
345-
notifier_title=notifier.title,
346-
),
347-
)
348-
# individual_result.result is updated in case of success
349-
individual_result = IndividualResult(
350-
notifier=notifier.name, title=notifier.title, result=None
351-
)
325+
log_extra = {
326+
"commit": commit.commitid,
327+
"base_commit": base_commit.commitid
328+
if base_commit is not None
329+
else "NO_BASE",
330+
"repoid": commit.repoid,
331+
"notifier": notifier.name,
332+
"notifier_title": notifier.title,
333+
}
334+
335+
log.info("Attempting individual notification", extra=log_extra)
336+
res: NotificationResult | None = None
352337
try:
353338
res = notifier.notify(
354339
comparison, status_or_checks_helper_text=status_or_checks_helper_text
355340
)
356-
individual_result["result"] = res
341+
log_extra["result"] = res
357342

343+
# TODO: The `CommentNotifier` is the only one implementing this method,
344+
# so maybe we should just move it there
358345
notifier.store_results(comparison, res)
359-
log.info(
360-
"Individual notification done",
361-
extra=dict(
362-
individual_result=individual_result,
363-
commit=commit.commitid,
364-
base_commit=(
365-
base_commit.commitid if base_commit is not None else "NO_BASE"
366-
),
367-
repoid=commit.repoid,
368-
),
369-
)
370-
return individual_result
346+
347+
log.info("Individual notification done", extra=log_extra)
348+
return notifier, res
371349
except (CeleryError, SoftTimeLimitExceeded):
372350
raise
373351
except Exception:
374-
log.exception(
375-
"Individual notifier failed",
376-
extra=dict(
377-
repoid=commit.repoid,
378-
commit=commit.commitid,
379-
individual_result=individual_result,
380-
base_commit=(
381-
base_commit.commitid if base_commit is not None else "NO_BASE"
382-
),
383-
),
384-
)
385-
return individual_result
352+
log.exception("Individual notifier failed", extra=log_extra)
353+
return notifier, res
386354
finally:
387-
if (
388-
individual_result["result"] is None
389-
or individual_result["result"].notification_attempted
390-
):
355+
if res is None or res.notification_attempted:
391356
# only running if there is no result (indicating some exception)
392357
# or there was an actual attempt
393358
create_or_update_commit_notification_from_notification_result(
394-
comparison, notifier, individual_result["result"]
359+
comparison, notifier, res
395360
)
361+
362+
363+
def split_notifiers(
364+
notifiers: Iterator[AbstractBaseNotifier],
365+
) -> tuple[list[AbstractBaseNotifier], list[AbstractBaseNotifier]]:
366+
"Splits the input notifiers based on whether they are a status/check, or other notifier."
367+
368+
status_or_checks_notifiers = []
369+
all_other_notifiers = []
370+
371+
for notifier in notifiers:
372+
if notifier.notification_type in notification_type_status_or_checks:
373+
status_or_checks_notifiers.append(notifier)
374+
else:
375+
all_other_notifiers.append(notifier)
376+
377+
return status_or_checks_notifiers, all_other_notifiers

services/notification/commit_notifications.py

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from database.enums import NotificationState
66
from database.models import CommitNotification, Pull
7-
from helpers.metrics import metrics
87
from services.comparison import ComparisonProxy
98
from services.notification.notifiers.base import (
109
AbstractBaseNotifier,
@@ -14,30 +13,11 @@
1413
log = logging.getLogger(__name__)
1514

1615

17-
def _get_notification_state_from_result(
18-
notification_result: NotificationResult | None,
19-
) -> NotificationState:
20-
"""
21-
Take notification result from notification service and convert to
22-
the proper NotificationState enum
23-
"""
24-
if notification_result is None:
25-
return NotificationState.error
26-
27-
successful = notification_result.notification_successful
28-
29-
if successful:
30-
return NotificationState.success
31-
else:
32-
return NotificationState.error
33-
34-
35-
@metrics.timer("internal.services.notification.store_notification_result")
3616
def create_or_update_commit_notification_from_notification_result(
3717
comparison: ComparisonProxy,
3818
notifier: AbstractBaseNotifier,
3919
notification_result: NotificationResult | None,
40-
) -> CommitNotification:
20+
) -> CommitNotification | None:
4121
"""Saves a CommitNotification entry in the database.
4222
We save an entry in the following scenarios:
4323
- We save all notification attempts for commits that are part of a PullRequest
@@ -54,12 +34,12 @@ def create_or_update_commit_notification_from_notification_result(
5434
or notification_result.notification_successful == False
5535
)
5636
if not_pull and (not_head_commit or not_github_app_info or failed):
57-
return
37+
return None
5838

5939
commit = pull.get_head_commit() if pull else comparison.head.commit
6040
if not commit:
6141
log.warning("Head commit not found for pull", extra=dict(pull=pull))
62-
return
42+
return None
6343

6444
db_session: Session = commit.get_db_session()
6545

@@ -72,7 +52,9 @@ def create_or_update_commit_notification_from_notification_result(
7252
.first()
7353
)
7454

75-
notification_state = _get_notification_state_from_result(notification_result)
55+
notification_state = (
56+
NotificationState.error if failed else NotificationState.success
57+
)
7658
github_app_used = (
7759
notification_result.github_app_used if notification_result else None
7860
)

services/notification/notifiers/comment/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343

4444
class CommentNotifier(MessageMixin, AbstractBaseNotifier):
45-
notify_conditions: list[NotifyCondition] = [
45+
notify_conditions: list[type[NotifyCondition]] = [
4646
ComparisonHasPull,
4747
PullRequestInProvider,
4848
PullHeadMatchesComparisonHead,

services/notification/notifiers/mixins/message/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ def create_message(
4949
pull = comparison.pull
5050

5151
settings = yaml_settings
52-
5352
current_yaml = self.current_yaml
5453

5554
links = {
@@ -63,7 +62,7 @@ def create_message(
6362
}
6463

6564
# bool: show complexity
66-
if read_yaml_field(self.current_yaml, ("codecov", "ui", "hide_complexity")):
65+
if read_yaml_field(current_yaml, ("codecov", "ui", "hide_complexity")):
6766
show_complexity = False
6867
else:
6968
show_complexity = bool(

services/notification/tests/unit/test_notification_service.py

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -610,11 +610,7 @@ def test_notify_individual_notifier_timeout(self, mocker, sample_comparison):
610610
res = notifications_service.notify_individual_notifier(
611611
notifier, sample_comparison
612612
)
613-
assert res == {
614-
"notifier": notifier.name,
615-
"result": None,
616-
"title": "fake_notifier",
617-
}
613+
assert res == (notifier, None)
618614

619615
@pytest.mark.django_db
620616
def test_notify_individual_checks_project_notifier(
@@ -649,10 +645,9 @@ def test_notify_individual_checks_project_notifier(
649645
notifier, sample_comparison
650646
)
651647

652-
assert res == {
653-
"notifier": "checks-project",
654-
"title": "title",
655-
"result": NotificationResult(
648+
assert res == (
649+
notifier,
650+
NotificationResult(
656651
notification_attempted=True,
657652
notification_successful=True,
658653
explanation=None,
@@ -668,7 +663,7 @@ def test_notify_individual_checks_project_notifier(
668663
},
669664
data_received=None,
670665
),
671-
}
666+
)
672667

673668
@pytest.mark.django_db
674669
def test_notify_individual_checks_patch_and_project_notifier_included_helper_text(
@@ -894,11 +889,8 @@ def test_notify_individual_notifier_timeout_notification_created(
894889
res = notifications_service.notify_individual_notifier(
895890
notifier, sample_comparison
896891
)
897-
assert res == {
898-
"notifier": notifier.name,
899-
"result": None,
900-
"title": "fake_notifier",
901-
}
892+
assert res == (notifier, None)
893+
902894
dbsession.flush()
903895
pull = sample_comparison.enriched_pull.database_pull
904896
pull_commit_notifications = pull.get_head_commit_notifications()
@@ -930,11 +922,8 @@ def test_notify_individual_notifier_notification_created_then_updated(
930922
res = notifications_service.notify_individual_notifier(
931923
notifier, sample_comparison
932924
)
933-
assert res == {
934-
"notifier": notifier.name,
935-
"result": None,
936-
"title": "fake_notifier",
937-
}
925+
assert res == (notifier, None)
926+
938927
dbsession.flush()
939928
pull = sample_comparison.enriched_pull.database_pull
940929
pull_commit_notifications = pull.get_head_commit_notifications()

0 commit comments

Comments
 (0)