oncall-engine/engine/apps/public_api/views/schedules.py
Vadim Stepanov a2eed312f9
PD migrator: migrate on-call shifts using public API (#1317)
Allow PD migrator tool to migrate on-call shifts when migrating
schedules (currently it migrates schedules using PD ICal file):
https://github.com/grafana/oncall/issues/1283.

This PR will allow to select the mode of schedule migration via
`SCHEDULE_MIGRATION_MODE_WEB` env variable (`ical` or `web`). Due to
differences in the scheduling systems of PD and OnCall, it's not always
possible to migrate shifts automatically (migration plan will show any
schedules and layers that can't be migrated).

PD rotations that will be possible to migrate:
- Any rotation without restrictions ("restriction" is a PD term for
describing active periods for rotation)
- Daily rotations with daily restrictions
- Weekly rotations with weekly restrictions
- Some weekly rotations with daily restrictions
- Some daily rotations with weekly restrictions

There will be a separate PR to update the
[instruction](https://github.com/grafana/oncall/tree/dev/tools/pagerduty-migrator#readme)
since this one is pretty huge already.
2023-02-23 11:34:03 +00:00

117 lines
4.5 KiB
Python

from django_filters import rest_framework as filters
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import Response
from rest_framework.viewsets import ModelViewSet
from apps.auth_token.auth import ApiTokenAuthentication, ScheduleExportAuthentication
from apps.public_api.custom_renderers import CalendarRenderer
from apps.public_api.serializers import PolymorphicScheduleSerializer, PolymorphicScheduleUpdateSerializer
from apps.public_api.throttlers.user_throttle import UserThrottle
from apps.schedules.ical_utils import ical_export_from_schedule
from apps.schedules.models import OnCallSchedule, OnCallScheduleWeb
from apps.slack.tasks import update_slack_user_group_for_schedules
from common.api_helpers.exceptions import BadRequest
from common.api_helpers.filters import ByTeamFilter
from common.api_helpers.mixins import RateLimitHeadersMixin, UpdateSerializerMixin
from common.api_helpers.paginators import FiftyPageSizePaginator
from common.insight_log import EntityEvent, write_resource_insight_log
class OnCallScheduleChannelView(RateLimitHeadersMixin, UpdateSerializerMixin, ModelViewSet):
authentication_classes = (ApiTokenAuthentication,)
permission_classes = (IsAuthenticated,)
throttle_classes = [UserThrottle]
model = OnCallSchedule
serializer_class = PolymorphicScheduleSerializer
update_serializer_class = PolymorphicScheduleUpdateSerializer
pagination_class = FiftyPageSizePaginator
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = ByTeamFilter
def get_queryset(self):
name = self.request.query_params.get("name", None)
queryset = OnCallSchedule.objects.filter(organization=self.request.auth.organization)
if name is not None:
queryset = queryset.filter(name=name)
return queryset.order_by("id")
def get_object(self):
public_primary_key = self.kwargs["pk"]
try:
return OnCallSchedule.objects.filter(
organization=self.request.auth.organization,
).get(public_primary_key=public_primary_key)
except OnCallSchedule.DoesNotExist:
raise NotFound
def perform_create(self, serializer):
serializer.save()
instance = serializer.instance
if instance.user_group is not None:
update_slack_user_group_for_schedules.apply_async((instance.user_group.pk,))
write_resource_insight_log(
instance=serializer.instance,
author=self.request.user,
event=EntityEvent.CREATED,
)
def perform_update(self, serializer):
if isinstance(serializer.instance, OnCallScheduleWeb):
raise BadRequest(detail="Web schedule update is not enabled through API")
prev_state = serializer.instance.insight_logs_serialized
old_user_group = serializer.instance.user_group
updated_schedule = serializer.save()
if old_user_group is not None:
update_slack_user_group_for_schedules.apply_async((old_user_group.pk,))
if updated_schedule.user_group is not None and updated_schedule.user_group != old_user_group:
update_slack_user_group_for_schedules.apply_async((updated_schedule.user_group.pk,))
new_state = serializer.instance.insight_logs_serialized
write_resource_insight_log(
instance=serializer.instance,
author=self.request.user,
event=EntityEvent.UPDATED,
prev_state=prev_state,
new_state=new_state,
)
def perform_destroy(self, instance):
write_resource_insight_log(
instance=instance,
author=self.request.user,
event=EntityEvent.DELETED,
)
instance.delete()
if instance.user_group is not None:
update_slack_user_group_for_schedules.apply_async((instance.user_group.pk,))
@action(
methods=["get"],
detail=True,
renderer_classes=(CalendarRenderer,),
authentication_classes=(ScheduleExportAuthentication,),
permission_classes=(IsAuthenticated,),
)
def export(self, request, pk):
# Not using existing get_object method because it requires access to the organization user attribute
export = ical_export_from_schedule(self.request.auth.schedule)
return Response(export, status=status.HTTP_200_OK)