Complete recurrence editing and calendar preferences
This commit is contained in:
@@ -33,6 +33,8 @@ from govoplan_calendar.backend.schemas import (
|
||||
CalendarEventCreateRequest,
|
||||
CalendarEventDeltaResponse,
|
||||
CalendarEventListResponse,
|
||||
CalendarEventOccurrenceDeleteRequest,
|
||||
CalendarEventOccurrenceUpdateRequest,
|
||||
CalendarEventResponse,
|
||||
CalendarEventUpdateRequest,
|
||||
CalendarFreeBusyRequest,
|
||||
@@ -49,6 +51,8 @@ from govoplan_calendar.backend.schemas import (
|
||||
CalendarSyncSourceSyncRequest,
|
||||
CalendarSyncSourceSyncResponse,
|
||||
CalendarSyncSourceUpdateRequest,
|
||||
CalendarViewPreferencesResponse,
|
||||
CalendarViewPreferencesUpdateRequest,
|
||||
)
|
||||
from govoplan_calendar.backend.outbox import (
|
||||
calendar_outbox_operation_response,
|
||||
@@ -75,14 +79,17 @@ from govoplan_calendar.backend.service import (
|
||||
delete_caldav_source,
|
||||
delete_sync_source,
|
||||
delete_event,
|
||||
delete_event_occurrence,
|
||||
discover_caldav_calendars,
|
||||
event_response,
|
||||
get_calendar_view_preferences,
|
||||
get_event,
|
||||
import_ics_event,
|
||||
list_freebusy,
|
||||
list_caldav_sources,
|
||||
list_calendars,
|
||||
list_events,
|
||||
list_event_occurrences,
|
||||
list_sync_sources,
|
||||
normalize_datetime,
|
||||
sync_due_sources,
|
||||
@@ -93,6 +100,8 @@ from govoplan_calendar.backend.service import (
|
||||
update_caldav_source,
|
||||
update_sync_source,
|
||||
update_event,
|
||||
update_event_occurrence,
|
||||
update_calendar_view_preferences,
|
||||
)
|
||||
from govoplan_core.db.session import get_session
|
||||
|
||||
@@ -743,10 +752,39 @@ def api_list_events(
|
||||
calendar_id: str | None = None,
|
||||
start_at: datetime | None = Query(default=None),
|
||||
end_at: datetime | None = Query(default=None),
|
||||
expand_recurring: bool = Query(default=False),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
_require_scope(principal, "calendar:event:read")
|
||||
if expand_recurring:
|
||||
if start_at is None or end_at is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail=(
|
||||
"start_at and end_at are required when expanding "
|
||||
"recurring events"
|
||||
),
|
||||
)
|
||||
try:
|
||||
occurrences = list_event_occurrences(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
calendar_id=calendar_id,
|
||||
start_at=start_at,
|
||||
end_at=end_at,
|
||||
)
|
||||
return CalendarEventListResponse(
|
||||
events=[
|
||||
CalendarEventResponse.model_validate(item)
|
||||
for item in occurrences
|
||||
]
|
||||
)
|
||||
except CalendarError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
events = list_events(session, tenant_id=principal.tenant_id, calendar_id=calendar_id, start_at=start_at, end_at=end_at)
|
||||
return CalendarEventListResponse(events=[_event_response(event) for event in events])
|
||||
|
||||
@@ -850,6 +888,70 @@ def api_update_event(
|
||||
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.patch(
|
||||
"/events/{series_event_id}/occurrence",
|
||||
response_model=CalendarEventResponse,
|
||||
)
|
||||
def api_update_event_occurrence(
|
||||
series_event_id: str,
|
||||
payload: CalendarEventOccurrenceUpdateRequest,
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
_require_scope(principal, CALENDAR_EVENT_WRITE_SCOPE)
|
||||
try:
|
||||
event = update_event_occurrence(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=principal.user.id,
|
||||
series_event_id=series_event_id,
|
||||
payload=payload,
|
||||
)
|
||||
session.commit()
|
||||
session.refresh(event)
|
||||
return _event_response(event)
|
||||
except CalendarError as exc:
|
||||
session.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/events/{series_event_id}/occurrence",
|
||||
response_model=CalendarEventResponse,
|
||||
)
|
||||
def api_delete_event_occurrence(
|
||||
series_event_id: str,
|
||||
payload: CalendarEventOccurrenceDeleteRequest,
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
_require_any_scope(
|
||||
principal,
|
||||
"calendar:event:delete",
|
||||
CALENDAR_EVENT_WRITE_SCOPE,
|
||||
)
|
||||
try:
|
||||
event = delete_event_occurrence(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=principal.user.id,
|
||||
series_event_id=series_event_id,
|
||||
recurrence_id=payload.recurrence_id,
|
||||
)
|
||||
session.commit()
|
||||
session.refresh(event)
|
||||
return _event_response(event)
|
||||
except CalendarError as exc:
|
||||
session.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
|
||||
|
||||
@router.delete("/events/{event_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def api_delete_event(
|
||||
event_id: str,
|
||||
@@ -866,6 +968,51 @@ def api_delete_event(
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/preferences/view",
|
||||
response_model=CalendarViewPreferencesResponse,
|
||||
)
|
||||
def api_get_calendar_view_preferences(
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
_require_scope(principal, "calendar:event:read")
|
||||
return CalendarViewPreferencesResponse.model_validate(
|
||||
get_calendar_view_preferences(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=principal.user.id,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@router.patch(
|
||||
"/preferences/view",
|
||||
response_model=CalendarViewPreferencesResponse,
|
||||
)
|
||||
def api_update_calendar_view_preferences(
|
||||
payload: CalendarViewPreferencesUpdateRequest,
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
_require_scope(principal, "calendar:event:read")
|
||||
try:
|
||||
response = update_calendar_view_preferences(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=principal.user.id,
|
||||
payload=payload,
|
||||
)
|
||||
session.commit()
|
||||
return CalendarViewPreferencesResponse.model_validate(response)
|
||||
except CalendarError as exc:
|
||||
session.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
|
||||
|
||||
@router.post("/events/import-ics", response_model=CalendarEventResponse, status_code=status.HTTP_201_CREATED)
|
||||
def api_import_ics_event(
|
||||
payload: CalendarIcsImportRequest,
|
||||
|
||||
Reference in New Issue
Block a user