diff --git a/CHANGELOG.md b/CHANGELOG.md index bacd23acc1..9ddbff4584 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix unicode characters not rendering correctly in webhooks @mderynck ([#3670](https://github.com/grafana/oncall/pull/3670)) +- Fix internal schedule detail API to set oncall_now for a schedule in orgs with multiple entries ([#3671](https://github.com/grafana/oncall/pull/3671)) ## v1.3.85 (2024-01-12) diff --git a/engine/apps/api/tests/test_schedules.py b/engine/apps/api/tests/test_schedules.py index a33ea55995..839f6e5801 100644 --- a/engine/apps/api/tests/test_schedules.py +++ b/engine/apps/api/tests/test_schedules.py @@ -565,6 +565,69 @@ def test_get_detail_web_schedule( assert response.data == expected_payload +@pytest.mark.django_db +def test_get_detail_schedule_oncall_now_multipage_objects( + make_organization_and_user_with_plugin_token, make_schedule, make_on_call_shift, make_user_auth_headers +): + organization, user, token = make_organization_and_user_with_plugin_token() + # make sure our schedule would be in the second page of the listing page + for i in range(16): + make_schedule(organization, schedule_class=OnCallScheduleWeb, name=f"schedule {i}") + + schedule = make_schedule( + organization, + schedule_class=OnCallScheduleWeb, + name="test_web_schedule", + ) + now = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) + start_date = now - timezone.timedelta(days=7) + data = { + "start": start_date, + "rotation_start": start_date, + "duration": timezone.timedelta(seconds=86400), + "priority_level": 1, + "frequency": CustomOnCallShift.FREQUENCY_DAILY, + "schedule": schedule, + } + on_call_shift = make_on_call_shift( + organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data + ) + on_call_shift.add_rolling_users([[user]]) + + client = APIClient() + url = reverse("api-internal:schedule-detail", kwargs={"pk": schedule.public_primary_key}) + + expected_payload = { + "id": schedule.public_primary_key, + "team": None, + "name": "test_web_schedule", + "type": 2, + "time_zone": "UTC", + "slack_channel": None, + "user_group": None, + "warnings": [], + "on_call_now": [ + { + "pk": user.public_primary_key, + "username": user.username, + "avatar": user.avatar_url, + "avatar_full": user.avatar_full_url, + } + ], + "has_gaps": False, + "mention_oncall_next": False, + "mention_oncall_start": True, + "notify_empty_oncall": 0, + "notify_oncall_shift_freq": 1, + "number_of_escalation_chains": 0, + "enable_web_overrides": True, + } + + response = client.get(url, format="json", **make_user_auth_headers(user, token)) + assert response.status_code == status.HTTP_200_OK + assert response.data == expected_payload + + @pytest.mark.django_db def test_create_calendar_schedule(schedule_internal_api_setup, make_user_auth_headers): user, token, _, _, _, _ = schedule_internal_api_setup diff --git a/engine/apps/api/views/schedule.py b/engine/apps/api/views/schedule.py index 74908fe5f8..16047ec5a1 100644 --- a/engine/apps/api/views/schedule.py +++ b/engine/apps/api/views/schedule.py @@ -137,8 +137,14 @@ def oncall_users(self): The result of this method is cached and is reused for the whole lifetime of a request, since self.get_serializer_context() is called multiple times for every instance in the queryset. """ - current_page_schedules = self.paginate_queryset(self.filter_queryset(self.get_queryset(annotate=False))) - return get_oncall_users_for_multiple_schedules(current_page_schedules) + current_schedules = self.get_queryset(annotate=False).none() + if self.action == "list": + # listing page, only get oncall users for current page schedules + current_schedules = self.paginate_queryset(self.filter_queryset(self.get_queryset(annotate=False))) + elif self.kwargs.get("pk"): + # if this is a particular schedule detail, only consider it as current + current_schedules = [self.get_object(annotate=False)] + return get_oncall_users_for_multiple_schedules(current_schedules) def get_serializer_context(self): context = super().get_serializer_context()