Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
mwheeler-ep committed Dec 2, 2024
1 parent 0c811e0 commit b3914b8
Show file tree
Hide file tree
Showing 20 changed files with 296 additions and 171 deletions.
4 changes: 4 additions & 0 deletions docs/sources/oncall-api-reference/alertgroups.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,17 @@ The above command returns JSON structured in the following way:
]
}
},
"teams": [
"TE5EF3RQHJQPI"
]
}
],
"current_page_number": 1,
"page_size": 50,
"total_pages": 1
}
```
> **Note**: `team_id` is provided for each alert_group however this is based off the old method where team was assigned based on integration team. It's recommended to use the new `teams` field.
> **Note**: The response is [paginated](ref:pagination). You may need to make multiple requests to get all records.
Expand Down
19 changes: 19 additions & 0 deletions engine/apps/alerts/migrations/0065_alertgroup_teams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.16 on 2024-11-19 06:01

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('user_management', '0026_auto_20241017_1919'),
('alerts', '0064_migrate_resolutionnoteslackmessage_slack_channel_id'),
]

operations = [
migrations.AddField(
model_name='alertgroup',
name='teams',
field=models.ManyToManyField(to='user_management.team'),
),
]
18 changes: 18 additions & 0 deletions engine/apps/alerts/migrations/0066_channelfilter_update_team.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.16 on 2024-11-22 00:22

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('alerts', '0065_alertgroup_teams'),
]

operations = [
migrations.AddField(
model_name='channelfilter',
name='update_team',
field=models.BooleanField(default=False, null=True),
),
]
13 changes: 13 additions & 0 deletions engine/apps/alerts/models/alert.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,19 @@ def create(
group.log_records.create(type=AlertGroupLogRecord.TYPE_REGISTERED)
group.log_records.create(type=AlertGroupLogRecord.TYPE_ROUTE_ASSIGNED)

if group_created and alert_receive_channel.team:
# add the team from the integration if its available
group.teams.set([alert_receive_channel.team])
elif (
group_created
and channel_filter
and channel_filter.escalation_chain
and channel_filter.escalation_chain.team
and channel_filter.update_team
):
# set the team to the one defined in the escalation_chain if defined.
group.teams.set([channel_filter.escalation_chain.team])

if group_created or alert.group.pause_escalation:
# Build escalation snapshot if needed and start escalation
alert.group.start_escalation_if_needed(countdown=TASK_DELAY_SECONDS)
Expand Down
2 changes: 2 additions & 0 deletions engine/apps/alerts/models/alert_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ def status(self) -> int:

raw_escalation_snapshot = JSONField(null=True, default=None)

teams = models.ManyToManyField(to="user_management.Team")

# This field is used for constraints so we can use get_or_create() in concurrent calls
# https://docs.djangoproject.com/en/3.2/ref/models/querysets/#get-or-create
# Combined with unique_together below, it allows only one alert group with
Expand Down
4 changes: 2 additions & 2 deletions engine/apps/alerts/models/alert_receive_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,6 @@ def listen_for_alertreceivechannel_model_save(
# delete connected auth tokens
instance.auth_tokens.all().delete()

metrics_remove_deleted_integration_from_cache(instance)
metrics_remove_deleted_integration_from_cache(instance, instance.organization)
else:
metrics_update_integration_cache(instance)
metrics_update_integration_cache(instance, instance.organization)
3 changes: 3 additions & 0 deletions engine/apps/alerts/models/channel_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ class ChannelFilter(OrderedModel):
"alerts.EscalationChain", null=True, default=None, on_delete=models.SET_NULL, related_name="channel_filters"
)

# Should we update the alertgroup team when this route is used
update_team = models.BooleanField(null=True, default=False)

notify_in_slack = models.BooleanField(null=True, default=True)
notify_in_telegram = models.BooleanField(null=True, default=False)

Expand Down
20 changes: 17 additions & 3 deletions engine/apps/api/serializers/alert_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .alert_receive_channel import FastAlertReceiveChannelSerializer
from .alerts_field_cache_buster_mixin import AlertsFieldCacheBusterMixin
from .user import FastUserSerializer, UserShortSerializer
from .team import FastTeamSerializer

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
Expand Down Expand Up @@ -125,8 +126,7 @@ class AlertGroupListSerializer(
related_users = serializers.SerializerMethodField()
dependent_alert_groups = ShortAlertGroupSerializer(many=True)
root_alert_group = ShortAlertGroupSerializer()
team = TeamPrimaryKeyRelatedField(source="channel.team", allow_null=True)

teams = serializers.SerializerMethodField()
alerts_count = serializers.IntegerField(read_only=True)
render_for_web = serializers.SerializerMethodField()

Expand All @@ -136,6 +136,7 @@ class AlertGroupListSerializer(
"dependent_alert_groups",
"log_records__author",
"labels",
"teams",
Prefetch(
"slack_messages",
queryset=SlackMessage.objects.select_related("_slack_team_identity").order_by("created_at")[:1],
Expand Down Expand Up @@ -187,12 +188,24 @@ class Meta:
"root_alert_group",
"status",
"declare_incident_link",
"team",
"grafana_incident_id",
"labels",
"permalinks",
"teams"
]

@extend_schema_field(FastTeamSerializer(many=True))
def get_teams(self, obj: "AlertGroup"):
"""
Handle AlertGroups that haven't been assigned a team yet
"""

if obj.teams:
teams = obj.teams
elif obj.channel.team:
teams = [obj.channel.team]
return FastTeamSerializer(teams, context=self.context, many=True).data

def get_render_for_web(self, obj: "AlertGroup") -> RenderForWeb | EmptyRenderForWeb:
if not obj.last_alert:
return {}
Expand Down Expand Up @@ -231,6 +244,7 @@ def get_related_users(self, obj: "AlertGroup"):
return UserShortSerializer(users, context=self.context, many=True).data



class AlertGroupSerializer(AlertGroupListSerializer):
alerts = serializers.SerializerMethodField("get_limited_alerts")
last_alert_at = serializers.SerializerMethodField()
Expand Down
2 changes: 2 additions & 0 deletions engine/apps/api/serializers/channel_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class Meta:
"notification_backends",
"filtering_term_as_jinja2",
"telegram_channel_details",
"update_team"
]
read_only_fields = [
"created_at",
Expand Down Expand Up @@ -165,6 +166,7 @@ class Meta:
"notify_in_slack",
"notify_in_telegram",
"notification_backends",
"update_team"
]
read_only_fields = ["created_at", "is_default"]

Expand Down
23 changes: 10 additions & 13 deletions engine/apps/api/views/alert_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,24 +302,21 @@ def get_serializer_class(self):
def get_queryset(self, ignore_filtering_by_available_teams=False):
# no select_related or prefetch_related is used at this point, it will be done on paginate_queryset.

alert_receive_channels_qs = AlertReceiveChannel.objects_with_deleted.filter(
organization_id=self.request.auth.organization.id
)
if not ignore_filtering_by_available_teams:
alert_receive_channels_qs = alert_receive_channels_qs.filter(*self.available_teams_lookup_args)

# Filter by team(s). Since we really filter teams from integrations, this is not an AlertGroup model filter.
# This is based on the common.api_helpers.ByTeamModelFieldFilterMixin implementation
team_values = self.request.query_params.getlist("team", [])
if team_values:
null_team_lookup = Q(team__isnull=True) if NO_TEAM_VALUE in team_values else None
teams_lookup = Q(team__public_primary_key__in=[ppk for ppk in team_values if ppk != NO_TEAM_VALUE])
null_team_lookup = Q(teams__isnull=True) if NO_TEAM_VALUE in team_values else None
teams_lookup = Q(teams__public_primary_key__in=[ppk for ppk in team_values if ppk != NO_TEAM_VALUE])
if null_team_lookup:
teams_lookup = teams_lookup | null_team_lookup
alert_receive_channels_qs = alert_receive_channels_qs.filter(teams_lookup)

alert_receive_channels_ids = list(alert_receive_channels_qs.values_list("id", flat=True))
queryset = AlertGroup.objects.filter(channel__in=alert_receive_channels_ids)
if not ignore_filtering_by_available_teams:
queryset = AlertGroup.objects.filter(*self.available_teams_lookup_args)
else:
queryset = AlertGroup.objects

if team_values:
queryset = queryset.filter(teams_lookup)


if self.action in ("list", "stats") and not self.request.query_params.get("started_at"):
queryset = queryset.filter(started_at__gte=timezone.now() - timezone.timedelta(days=30))
Expand Down
Loading

0 comments on commit b3914b8

Please sign in to comment.