refactor: simplify ICS calendar API to return only duty shifts
All checks were successful
CI / lint-and-test (push) Successful in 24s
All checks were successful
CI / lint-and-test (push) Successful in 24s
- Removed the ability to specify multiple event types in the ICS calendar generation API, ensuring it only returns duty shifts. - Updated the associated test to reflect the change in behavior, confirming that unknown query parameters are ignored. - Revised documentation to clarify the API's focus on duty shifts only, enhancing clarity for users.
This commit is contained in:
@@ -3,8 +3,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from typing import Literal
|
|
||||||
|
|
||||||
import duty_teller.config as config
|
import duty_teller.config as config
|
||||||
from fastapi import Depends, FastAPI, Request
|
from fastapi import Depends, FastAPI, Request
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
@@ -100,20 +98,16 @@ def list_calendar_events(
|
|||||||
"/api/calendar/ical/{token}.ics",
|
"/api/calendar/ical/{token}.ics",
|
||||||
summary="Personal calendar ICS",
|
summary="Personal calendar ICS",
|
||||||
description=(
|
description=(
|
||||||
"Returns an ICS calendar with the subscribing user's events. "
|
"Returns an ICS calendar with the subscribing user's duty shifts only. "
|
||||||
"By default only duty shifts are included; use query parameter events=all "
|
|
||||||
"for all event types (duty, unavailable, vacation). "
|
|
||||||
"No Telegram auth; access is by secret token in the URL."
|
"No Telegram auth; access is by secret token in the URL."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def get_personal_calendar_ical(
|
def get_personal_calendar_ical(
|
||||||
token: str,
|
token: str,
|
||||||
events: Literal["duty", "all"] = "duty",
|
|
||||||
session: Session = Depends(get_db_session),
|
session: Session = Depends(get_db_session),
|
||||||
) -> Response:
|
) -> Response:
|
||||||
"""
|
"""
|
||||||
Return ICS calendar with the subscribing user's events.
|
Return ICS calendar with the subscribing user's duty shifts only.
|
||||||
Default: only duty shifts. Use ?events=all for duty, unavailable, vacation.
|
|
||||||
No Telegram auth; access is by secret token in the URL.
|
No Telegram auth; access is by secret token in the URL.
|
||||||
"""
|
"""
|
||||||
if not _is_valid_calendar_token(token):
|
if not _is_valid_calendar_token(token):
|
||||||
@@ -124,9 +118,8 @@ def get_personal_calendar_ical(
|
|||||||
today = date.today()
|
today = date.today()
|
||||||
from_date = (today - timedelta(days=365)).strftime("%Y-%m-%d")
|
from_date = (today - timedelta(days=365)).strftime("%Y-%m-%d")
|
||||||
to_date = (today + timedelta(days=365 * 2)).strftime("%Y-%m-%d")
|
to_date = (today + timedelta(days=365 * 2)).strftime("%Y-%m-%d")
|
||||||
event_types = ["duty"] if events == "duty" else None
|
|
||||||
duties_with_name = get_duties_for_user(
|
duties_with_name = get_duties_for_user(
|
||||||
session, user.id, from_date=from_date, to_date=to_date, event_types=event_types
|
session, user.id, from_date=from_date, to_date=to_date, event_types=["duty"]
|
||||||
)
|
)
|
||||||
ics_bytes = build_personal_ics(duties_with_name)
|
ics_bytes = build_personal_ics(duties_with_name)
|
||||||
return Response(
|
return Response(
|
||||||
|
|||||||
@@ -336,10 +336,10 @@ def test_calendar_ical_200_returns_only_that_users_duties(
|
|||||||
@patch("duty_teller.api.app.build_personal_ics")
|
@patch("duty_teller.api.app.build_personal_ics")
|
||||||
@patch("duty_teller.api.app.get_duties_for_user")
|
@patch("duty_teller.api.app.get_duties_for_user")
|
||||||
@patch("duty_teller.api.app.get_user_by_calendar_token")
|
@patch("duty_teller.api.app.get_user_by_calendar_token")
|
||||||
def test_calendar_ical_events_all_returns_all_event_types(
|
def test_calendar_ical_ignores_unknown_query_params(
|
||||||
mock_get_user, mock_get_duties, mock_build_ics, client
|
mock_get_user, mock_get_duties, mock_build_ics, client
|
||||||
):
|
):
|
||||||
"""GET /api/calendar/ical/{token}.ics?events=all returns ICS with duty, unavailable, vacation."""
|
"""Unknown query params (e.g. events=all) are ignored; response is duty-only."""
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
mock_user = SimpleNamespace(id=1, full_name="User A")
|
mock_user = SimpleNamespace(id=1, full_name="User A")
|
||||||
@@ -351,39 +351,15 @@ def test_calendar_ical_events_all_returns_all_event_types(
|
|||||||
end_at="2026-06-15T18:00:00Z",
|
end_at="2026-06-15T18:00:00Z",
|
||||||
event_type="duty",
|
event_type="duty",
|
||||||
)
|
)
|
||||||
unavailable = SimpleNamespace(
|
mock_get_duties.return_value = [(duty, "User A")]
|
||||||
id=11,
|
mock_build_ics.return_value = b"BEGIN:VCALENDAR\r\nEND:VCALENDAR"
|
||||||
user_id=1,
|
token = "z" * 43
|
||||||
start_at="2026-06-16T09:00:00Z",
|
|
||||||
end_at="2026-06-16T18:00:00Z",
|
|
||||||
event_type="unavailable",
|
|
||||||
)
|
|
||||||
vacation = SimpleNamespace(
|
|
||||||
id=12,
|
|
||||||
user_id=1,
|
|
||||||
start_at="2026-06-17T09:00:00Z",
|
|
||||||
end_at="2026-06-17T18:00:00Z",
|
|
||||||
event_type="vacation",
|
|
||||||
)
|
|
||||||
mock_get_duties.return_value = [
|
|
||||||
(duty, "User A"),
|
|
||||||
(unavailable, "User A"),
|
|
||||||
(vacation, "User A"),
|
|
||||||
]
|
|
||||||
mock_build_ics.return_value = b"BEGIN:VCALENDAR\r\nVEVENT\r\nEND:VCALENDAR"
|
|
||||||
token = "y" * 43
|
|
||||||
|
|
||||||
r = client.get(f"/api/calendar/ical/{token}.ics", params={"events": "all"})
|
r = client.get(f"/api/calendar/ical/{token}.ics", params={"events": "all"})
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert r.headers.get("content-type", "").startswith("text/calendar")
|
|
||||||
mock_get_duties.assert_called_once_with(
|
mock_get_duties.assert_called_once_with(
|
||||||
ANY, 1, from_date=ANY, to_date=ANY, event_types=None
|
ANY, 1, from_date=ANY, to_date=ANY, event_types=["duty"]
|
||||||
)
|
)
|
||||||
duties_arg = mock_build_ics.call_args[0][0]
|
|
||||||
assert len(duties_arg) == 3
|
|
||||||
assert duties_arg[0][0].event_type == "duty"
|
|
||||||
assert duties_arg[1][0].event_type == "unavailable"
|
|
||||||
assert duties_arg[2][0].event_type == "vacation"
|
|
||||||
|
|
||||||
|
|
||||||
# --- /api/calendar-events ---
|
# --- /api/calendar-events ---
|
||||||
|
|||||||
Reference in New Issue
Block a user