oncall-engine/engine/common/ordered_model/viewset.py
Vadim Stepanov 602ed535e3
Fix duplicate orders on routes and escalation policies (#2568)
# What this PR does

Fix duplicate `order` values for models `EscalationPolicy` and
`ChannelFilter` using the same approach as
https://github.com/grafana/oncall/pull/2278.

- Make internal API action `move_to_position` a part of
[OrderedModelViewSet](https://github.com/grafana/oncall/pull/2568/files#diff-eb62521ccbcb072d1bd2156adeadae3b5895bce6d0d54432a23db3948b0ada54R11-R34),
so all ordered model views use the same logic.
- Make public API serializers for ordered models inherit from
[OrderedModelSerializer](https://github.com/grafana/oncall/pull/2568/files#diff-d749f94af5a49adaf5074325cdfad10ddd5a52dbfd78b49582867ebb9c92fae5R6-R38),
so ordered model views are consistent with each other in public API.
- Remove `order` from plugin fronted, since it's not being used
anywhere. The frontend uses step indices & `move_to_position` action
instead.
- Make escalation snapshot use step indices instead of orders, since
orders are not guaranteed to be sequential (+fix a minor off-by-one bug)

## Which issue(s) this PR fixes

https://github.com/grafana/oncall-private/issues/1680

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
2023-07-18 17:17:53 +00:00

56 lines
2 KiB
Python

from rest_framework import serializers, status
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from common.api_helpers.exceptions import BadRequest
from common.insight_log import EntityEvent, write_resource_insight_log
class OrderedModelViewSet(ModelViewSet):
"""Ordered model viewset to be used in internal API."""
@action(detail=True, methods=["put"])
def move_to_position(self, request: Request, pk: int) -> Response:
instance = self.get_object()
position = self._get_move_to_position_param(request)
prev_state = self._get_insight_logs_serialized(instance)
try:
instance.to_index(position)
except IndexError:
raise BadRequest(detail="Invalid position")
new_state = self._get_insight_logs_serialized(instance)
write_resource_insight_log(
instance=instance,
author=self.request.user,
event=EntityEvent.UPDATED,
prev_state=prev_state,
new_state=new_state,
)
return Response(status=status.HTTP_200_OK)
@staticmethod
def _get_insight_logs_serialized(instance):
try:
return instance.insight_logs_serialized
except AttributeError:
return instance.user.insight_logs_serialized # workaround for UserNotificationPolicy
@staticmethod
def _get_move_to_position_param(request: Request) -> int:
"""
Get "position" parameter from query params + validate it.
Used by actions on ordered models (e.g. move_to_position).
"""
class MoveToPositionQueryParamsSerializer(serializers.Serializer):
position = serializers.IntegerField()
serializer = MoveToPositionQueryParamsSerializer(data=request.query_params)
serializer.is_valid(raise_exception=True)
return serializer.validated_data["position"]